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