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<'c>(
30 cx: &'c mut AcceptContext<'_, '_, S>,
31 args: &'c ArgParser<'_>,
32 ) -> impl IntoIterator<Item = Self::Item> + 'c {
33 let mut reprs = Vec::new();
34
35 let Some(list) = args.list() else {
36 cx.expected_list(cx.attr_span);
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>(
102 cx: &AcceptContext<'_, '_, S>,
103 param: &MetaItemParser<'_>,
104) -> Option<ReprAttr> {
105 use ReprAttr::*;
106
107 let (name, ident_span) = if let Some(ident) = param.path().word() {
110 (Some(ident.name), ident.span)
111 } else {
112 (None, DUMMY_SP)
113 };
114
115 let args = param.args();
116
117 match (name, args) {
118 (Some(sym::align), ArgParser::NoArgs) => {
119 cx.emit_err(session_diagnostics::InvalidReprAlignNeedArg { span: ident_span });
120 None
121 }
122 (Some(sym::align), ArgParser::List(l)) => {
123 parse_repr_align(cx, l, param.span(), AlignKind::Align)
124 }
125
126 (Some(sym::packed), ArgParser::NoArgs) => Some(ReprPacked(Align::ONE)),
127 (Some(sym::packed), ArgParser::List(l)) => {
128 parse_repr_align(cx, l, param.span(), AlignKind::Packed)
129 }
130
131 (Some(name @ sym::align | name @ sym::packed), ArgParser::NameValue(l)) => {
132 cx.emit_err(session_diagnostics::IncorrectReprFormatGeneric {
133 span: param.span(),
134 repr_arg: name,
136 cause: IncorrectReprFormatGenericCause::from_lit_kind(
137 param.span(),
138 &l.value_as_lit().kind,
139 name,
140 ),
141 });
142 None
143 }
144
145 (Some(sym::Rust), ArgParser::NoArgs) => Some(ReprRust),
146 (Some(sym::C), ArgParser::NoArgs) => Some(ReprC),
147 (Some(sym::simd), ArgParser::NoArgs) => Some(ReprSimd),
148 (Some(sym::transparent), ArgParser::NoArgs) => Some(ReprTransparent),
149 (Some(name @ int_pat!()), ArgParser::NoArgs) => {
150 Some(ReprInt(int_type_of_word(name).unwrap()))
152 }
153
154 (
155 Some(
156 name @ sym::Rust
157 | name @ sym::C
158 | name @ sym::simd
159 | name @ sym::transparent
160 | name @ int_pat!(),
161 ),
162 ArgParser::NameValue(_),
163 ) => {
164 cx.emit_err(session_diagnostics::InvalidReprHintNoValue { span: param.span(), name });
165 None
166 }
167 (
168 Some(
169 name @ sym::Rust
170 | name @ sym::C
171 | name @ sym::simd
172 | name @ sym::transparent
173 | name @ int_pat!(),
174 ),
175 ArgParser::List(_),
176 ) => {
177 cx.emit_err(session_diagnostics::InvalidReprHintNoParen { span: param.span(), name });
178 None
179 }
180
181 _ => {
182 cx.emit_err(session_diagnostics::UnrecognizedReprHint { span: param.span() });
183 None
184 }
185 }
186}
187
188enum AlignKind {
189 Packed,
190 Align,
191}
192
193fn parse_repr_align<S: Stage>(
194 cx: &AcceptContext<'_, '_, S>,
195 list: &MetaItemListParser<'_>,
196 param_span: Span,
197 align_kind: AlignKind,
198) -> Option<ReprAttr> {
199 use AlignKind::*;
200
201 let Some(align) = list.single() else {
202 match align_kind {
203 Packed => {
204 cx.emit_err(session_diagnostics::IncorrectReprFormatPackedOneOrZeroArg {
205 span: param_span,
206 });
207 }
208 Align => {
209 cx.emit_err(session_diagnostics::IncorrectReprFormatAlignOneArg {
210 span: param_span,
211 });
212 }
213 }
214
215 return None;
216 };
217
218 let Some(lit) = align.lit() else {
219 match align_kind {
220 Packed => {
221 cx.emit_err(session_diagnostics::IncorrectReprFormatPackedExpectInteger {
222 span: align.span(),
223 });
224 }
225 Align => {
226 cx.emit_err(session_diagnostics::IncorrectReprFormatExpectInteger {
227 span: align.span(),
228 });
229 }
230 }
231
232 return None;
233 };
234
235 match parse_alignment(&lit.kind) {
236 Ok(literal) => Some(match align_kind {
237 AlignKind::Packed => ReprAttr::ReprPacked(literal),
238 AlignKind::Align => ReprAttr::ReprAlign(literal),
239 }),
240 Err(message) => {
241 cx.emit_err(session_diagnostics::InvalidReprGeneric {
242 span: lit.span,
243 repr_arg: match align_kind {
244 Packed => "packed".to_string(),
245 Align => "align".to_string(),
246 },
247 error_part: message,
248 });
249 None
250 }
251 }
252}
253
254fn parse_alignment(node: &LitKind) -> Result<Align, &'static str> {
255 if let LitKind::Int(literal, LitIntType::Unsuffixed) = node {
256 if literal.get().is_power_of_two() {
258 literal
260 .get()
261 .try_into()
262 .ok()
263 .and_then(|v| Align::from_bytes(v).ok())
264 .ok_or("larger than 2^29")
265 } else {
266 Err("not a power of two")
267 }
268 } else {
269 Err("not an unsuffixed integer")
270 }
271}
272
273#[derive(Default)]
275pub(crate) struct AlignParser(Option<(Align, Span)>);
276
277impl AlignParser {
278 const PATH: &'static [Symbol] = &[sym::rustc_align];
279 const TEMPLATE: AttributeTemplate = template!(List: &["<alignment in bytes>"]);
280
281 fn parse<'c, S: Stage>(
282 &mut self,
283 cx: &'c mut AcceptContext<'_, '_, S>,
284 args: &'c ArgParser<'_>,
285 ) {
286 match args {
287 ArgParser::NoArgs | ArgParser::NameValue(_) => {
288 cx.expected_list(cx.attr_span);
289 }
290 ArgParser::List(list) => {
291 let Some(align) = list.single() else {
292 cx.expected_single_argument(list.span);
293 return;
294 };
295
296 let Some(lit) = align.lit() else {
297 cx.emit_err(session_diagnostics::IncorrectReprFormatExpectInteger {
298 span: align.span(),
299 });
300
301 return;
302 };
303
304 match parse_alignment(&lit.kind) {
305 Ok(literal) => self.0 = Ord::max(self.0, Some((literal, cx.attr_span))),
306 Err(message) => {
307 cx.emit_err(session_diagnostics::InvalidAlignmentValue {
308 span: lit.span,
309 error_part: message,
310 });
311 }
312 }
313 }
314 }
315 }
316}
317
318impl<S: Stage> AttributeParser<S> for AlignParser {
319 const ATTRIBUTES: AcceptMapping<Self, S> = &[(Self::PATH, Self::TEMPLATE, Self::parse)];
320 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
321 Allow(Target::Fn),
322 Allow(Target::Method(MethodKind::Inherent)),
323 Allow(Target::Method(MethodKind::Trait { body: true })),
324 Allow(Target::Method(MethodKind::TraitImpl)),
325 Allow(Target::Method(MethodKind::Trait { body: false })),
326 Allow(Target::ForeignFn),
327 ]);
328
329 fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
330 let (align, span) = self.0?;
331 Some(AttributeKind::Align { align, span })
332 }
333}