rustc_attr_parsing/attributes/
repr.rs

1use 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
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 = 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    // FIXME(jdonszelmann): invert the parsing here to match on the word first and then the
109    // structure.
110    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                // FIXME(jdonszelmann) can just be a string in the diag type
136                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            // int_pat!() should make sure it always parses
152            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        // `Align::from_bytes` accepts 0 as an input, check is_power_of_two() first
258        if literal.get().is_power_of_two() {
259            // Only possible error is larger than 2^29
260            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/// Parse #[align(N)].
275#[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}