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