rustc_attr_parsing/attributes/
repr.rs1use rustc_abi::Align;
2use rustc_ast::{IntTy, LitIntType, LitKind, UintTy};
3use rustc_hir::attrs::{IntType, ReprAttr};
4
5use super::prelude::*;
6use crate::session_diagnostics::{self, IncorrectReprFormatGenericCause};
7
8pub(crate) struct ReprParser;
17
18impl<S: Stage> CombineAttributeParser<S> for ReprParser {
19 type Item = (ReprAttr, Span);
20 const PATH: &[Symbol] = &[sym::repr];
21 const CONVERT: ConvertFn<Self::Item> =
22 |items, first_span| AttributeKind::Repr { reprs: items, first_span };
23 const TEMPLATE: AttributeTemplate = template!(
25 List: &["C", "Rust", "transparent", "align(...)", "packed(...)", "<integer type>"],
26 "https://doc.rust-lang.org/reference/type-layout.html#representations"
27 );
28
29 fn extend(
30 cx: &mut AcceptContext<'_, '_, S>,
31 args: &ArgParser,
32 ) -> impl IntoIterator<Item = Self::Item> {
33 let mut reprs = Vec::new();
34
35 let Some(list) = args.list() else {
36 cx.expected_list(cx.attr_span, args);
37 return reprs;
38 };
39
40 if list.is_empty() {
41 cx.warn_empty_attribute(cx.attr_span);
42 return reprs;
43 }
44
45 for param in list.mixed() {
46 if let Some(_) = param.lit() {
47 cx.emit_err(session_diagnostics::ReprIdent { span: cx.attr_span });
48 continue;
49 }
50
51 reprs.extend(
52 param.meta_item().and_then(|mi| parse_repr(cx, &mi)).map(|r| (r, param.span())),
53 );
54 }
55
56 reprs
57 }
58
59 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
62}
63
64macro_rules! int_pat {
65 () => {
66 sym::i8
67 | sym::u8
68 | sym::i16
69 | sym::u16
70 | sym::i32
71 | sym::u32
72 | sym::i64
73 | sym::u64
74 | sym::i128
75 | sym::u128
76 | sym::isize
77 | sym::usize
78 };
79}
80
81fn int_type_of_word(s: Symbol) -> Option<IntType> {
82 use IntType::*;
83
84 match s {
85 sym::i8 => Some(SignedInt(IntTy::I8)),
86 sym::u8 => Some(UnsignedInt(UintTy::U8)),
87 sym::i16 => Some(SignedInt(IntTy::I16)),
88 sym::u16 => Some(UnsignedInt(UintTy::U16)),
89 sym::i32 => Some(SignedInt(IntTy::I32)),
90 sym::u32 => Some(UnsignedInt(UintTy::U32)),
91 sym::i64 => Some(SignedInt(IntTy::I64)),
92 sym::u64 => Some(UnsignedInt(UintTy::U64)),
93 sym::i128 => Some(SignedInt(IntTy::I128)),
94 sym::u128 => Some(UnsignedInt(UintTy::U128)),
95 sym::isize => Some(SignedInt(IntTy::Isize)),
96 sym::usize => Some(UnsignedInt(UintTy::Usize)),
97 _ => None,
98 }
99}
100
101fn parse_repr<S: Stage>(cx: &AcceptContext<'_, '_, S>, param: &MetaItemParser) -> Option<ReprAttr> {
102 use ReprAttr::*;
103
104 let (name, ident_span) = if let Some(ident) = param.path().word() {
107 (Some(ident.name), ident.span)
108 } else {
109 (None, DUMMY_SP)
110 };
111
112 let args = param.args();
113
114 match (name, args) {
115 (Some(sym::align), ArgParser::NoArgs) => {
116 cx.emit_err(session_diagnostics::InvalidReprAlignNeedArg { span: ident_span });
117 None
118 }
119 (Some(sym::align), ArgParser::List(l)) => {
120 parse_repr_align(cx, l, param.span(), AlignKind::Align)
121 }
122
123 (Some(sym::packed), ArgParser::NoArgs) => Some(ReprPacked(Align::ONE)),
124 (Some(sym::packed), ArgParser::List(l)) => {
125 parse_repr_align(cx, l, param.span(), AlignKind::Packed)
126 }
127
128 (Some(name @ sym::align | name @ sym::packed), ArgParser::NameValue(l)) => {
129 cx.emit_err(session_diagnostics::IncorrectReprFormatGeneric {
130 span: param.span(),
131 repr_arg: name,
133 cause: IncorrectReprFormatGenericCause::from_lit_kind(
134 param.span(),
135 &l.value_as_lit().kind,
136 name,
137 ),
138 });
139 None
140 }
141
142 (Some(sym::Rust), ArgParser::NoArgs) => Some(ReprRust),
143 (Some(sym::C), ArgParser::NoArgs) => Some(ReprC),
144 (Some(sym::simd), ArgParser::NoArgs) => Some(ReprSimd),
145 (Some(sym::transparent), ArgParser::NoArgs) => Some(ReprTransparent),
146 (Some(name @ int_pat!()), ArgParser::NoArgs) => {
147 Some(ReprInt(int_type_of_word(name).unwrap()))
149 }
150
151 (
152 Some(
153 name @ sym::Rust
154 | name @ sym::C
155 | name @ sym::simd
156 | name @ sym::transparent
157 | name @ int_pat!(),
158 ),
159 ArgParser::NameValue(_),
160 ) => {
161 cx.emit_err(session_diagnostics::InvalidReprHintNoValue { span: param.span(), name });
162 None
163 }
164 (
165 Some(
166 name @ sym::Rust
167 | name @ sym::C
168 | name @ sym::simd
169 | name @ sym::transparent
170 | name @ int_pat!(),
171 ),
172 ArgParser::List(_),
173 ) => {
174 cx.emit_err(session_diagnostics::InvalidReprHintNoParen { span: param.span(), name });
175 None
176 }
177
178 _ => {
179 cx.emit_err(session_diagnostics::UnrecognizedReprHint { span: param.span() });
180 None
181 }
182 }
183}
184
185enum AlignKind {
186 Packed,
187 Align,
188}
189
190fn parse_repr_align<S: Stage>(
191 cx: &AcceptContext<'_, '_, S>,
192 list: &MetaItemListParser,
193 param_span: Span,
194 align_kind: AlignKind,
195) -> Option<ReprAttr> {
196 use AlignKind::*;
197
198 let Some(align) = list.single() else {
199 match align_kind {
200 Packed => {
201 cx.emit_err(session_diagnostics::IncorrectReprFormatPackedOneOrZeroArg {
202 span: param_span,
203 });
204 }
205 Align => {
206 cx.emit_err(session_diagnostics::IncorrectReprFormatAlignOneArg {
207 span: param_span,
208 });
209 }
210 }
211
212 return None;
213 };
214
215 let Some(lit) = align.lit() else {
216 match align_kind {
217 Packed => {
218 cx.emit_err(session_diagnostics::IncorrectReprFormatPackedExpectInteger {
219 span: align.span(),
220 });
221 }
222 Align => {
223 cx.emit_err(session_diagnostics::IncorrectReprFormatExpectInteger {
224 span: align.span(),
225 });
226 }
227 }
228
229 return None;
230 };
231
232 match parse_alignment(&lit.kind) {
233 Ok(literal) => Some(match align_kind {
234 AlignKind::Packed => ReprAttr::ReprPacked(literal),
235 AlignKind::Align => ReprAttr::ReprAlign(literal),
236 }),
237 Err(message) => {
238 cx.emit_err(session_diagnostics::InvalidReprGeneric {
239 span: lit.span,
240 repr_arg: match align_kind {
241 Packed => "packed".to_string(),
242 Align => "align".to_string(),
243 },
244 error_part: message,
245 });
246 None
247 }
248 }
249}
250
251fn parse_alignment(node: &LitKind) -> Result<Align, &'static str> {
252 if let LitKind::Int(literal, LitIntType::Unsuffixed) = node {
253 if literal.get().is_power_of_two() {
255 literal
257 .get()
258 .try_into()
259 .ok()
260 .and_then(|v| Align::from_bytes(v).ok())
261 .ok_or("larger than 2^29")
262 } else {
263 Err("not a power of two")
264 }
265 } else {
266 Err("not an unsuffixed integer")
267 }
268}
269
270#[derive(Default)]
272pub(crate) struct AlignParser(Option<(Align, Span)>);
273
274impl AlignParser {
275 const PATH: &'static [Symbol] = &[sym::rustc_align];
276 const TEMPLATE: AttributeTemplate = template!(List: &["<alignment in bytes>"]);
277
278 fn parse<S: Stage>(&mut self, cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) {
279 match args {
280 ArgParser::NoArgs | ArgParser::NameValue(_) => {
281 cx.expected_list(cx.attr_span, args);
282 }
283 ArgParser::List(list) => {
284 let Some(align) = list.single() else {
285 cx.expected_single_argument(list.span);
286 return;
287 };
288
289 let Some(lit) = align.lit() else {
290 cx.emit_err(session_diagnostics::IncorrectReprFormatExpectInteger {
291 span: align.span(),
292 });
293
294 return;
295 };
296
297 match parse_alignment(&lit.kind) {
298 Ok(literal) => self.0 = Ord::max(self.0, Some((literal, cx.attr_span))),
299 Err(message) => {
300 cx.emit_err(session_diagnostics::InvalidAlignmentValue {
301 span: lit.span,
302 error_part: message,
303 });
304 }
305 }
306 }
307 }
308 }
309}
310
311impl<S: Stage> AttributeParser<S> for AlignParser {
312 const ATTRIBUTES: AcceptMapping<Self, S> = &[(Self::PATH, Self::TEMPLATE, 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<'_, '_, S>) -> Option<AttributeKind> {
323 let (align, span) = self.0?;
324 Some(AttributeKind::Align { align, span })
325 }
326}
327
328#[derive(Default)]
329pub(crate) struct AlignStaticParser(AlignParser);
330
331impl AlignStaticParser {
332 const PATH: &'static [Symbol] = &[sym::rustc_align_static];
333 const TEMPLATE: AttributeTemplate = AlignParser::TEMPLATE;
334
335 fn parse<S: Stage>(&mut self, cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) {
336 self.0.parse(cx, args)
337 }
338}
339
340impl<S: Stage> AttributeParser<S> for AlignStaticParser {
341 const ATTRIBUTES: AcceptMapping<Self, S> = &[(Self::PATH, Self::TEMPLATE, Self::parse)];
342 const ALLOWED_TARGETS: AllowedTargets =
343 AllowedTargets::AllowList(&[Allow(Target::Static), Allow(Target::ForeignStatic)]);
344
345 fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
346 let (align, span) = self.0.0?;
347 Some(AttributeKind::Align { align, span })
348 }
349}