rustc_attr_parsing/attributes/
repr.rs

1use 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
13/// Parse #[repr(...)] forms.
14///
15/// Valid repr contents: any of the primitive integral type names (see
16/// `int_type_of_word`, below) to specify enum discriminant type; `C`, to use
17/// the same discriminant size that the corresponding C enum would or C
18/// structure layout, `packed` to remove padding, and `transparent` to delegate representation
19/// concerns to the only non-ZST field.
20// FIXME(jdonszelmann): is a vec the right representation here even? isn't it just a struct?
21pub(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    // FIXME(jdonszelmann): never used
29    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    // FIXME(jdonszelmann): invert the parsing here to match on the word first and then the
107    // structure.
108    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                // FIXME(jdonszelmann) can just be a string in the diag type
134                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            // int_pat!() should make sure it always parses
150            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        // `Align::from_bytes` accepts 0 as an input, check is_power_of_two() first
256        if literal.get().is_power_of_two() {
257            // Only possible error is larger than 2^29
258            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/// Parse #[align(N)].
273#[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}