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