rustc_attr_parsing/attributes/
repr.rs

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