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