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<'c>(
30        cx: &'c mut AcceptContext<'_, '_, S>,
31        args: &'c ArgParser<'_>,
32    ) -> impl IntoIterator<Item = Self::Item> + 'c {
33        let mut reprs = Vec::new();
34
35        let Some(list) = args.list() else {
36            cx.expected_list(cx.attr_span);
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>(
102    cx: &AcceptContext<'_, '_, S>,
103    param: &MetaItemParser<'_>,
104) -> Option<ReprAttr> {
105    use ReprAttr::*;
106
107    // FIXME(jdonszelmann): invert the parsing here to match on the word first and then the
108    // structure.
109    let (name, ident_span) = if let Some(ident) = param.path().word() {
110        (Some(ident.name), ident.span)
111    } else {
112        (None, DUMMY_SP)
113    };
114
115    let args = param.args();
116
117    match (name, args) {
118        (Some(sym::align), ArgParser::NoArgs) => {
119            cx.emit_err(session_diagnostics::InvalidReprAlignNeedArg { span: ident_span });
120            None
121        }
122        (Some(sym::align), ArgParser::List(l)) => {
123            parse_repr_align(cx, l, param.span(), AlignKind::Align)
124        }
125
126        (Some(sym::packed), ArgParser::NoArgs) => Some(ReprPacked(Align::ONE)),
127        (Some(sym::packed), ArgParser::List(l)) => {
128            parse_repr_align(cx, l, param.span(), AlignKind::Packed)
129        }
130
131        (Some(name @ sym::align | name @ sym::packed), ArgParser::NameValue(l)) => {
132            cx.emit_err(session_diagnostics::IncorrectReprFormatGeneric {
133                span: param.span(),
134                // FIXME(jdonszelmann) can just be a string in the diag type
135                repr_arg: name,
136                cause: IncorrectReprFormatGenericCause::from_lit_kind(
137                    param.span(),
138                    &l.value_as_lit().kind,
139                    name,
140                ),
141            });
142            None
143        }
144
145        (Some(sym::Rust), ArgParser::NoArgs) => Some(ReprRust),
146        (Some(sym::C), ArgParser::NoArgs) => Some(ReprC),
147        (Some(sym::simd), ArgParser::NoArgs) => Some(ReprSimd),
148        (Some(sym::transparent), ArgParser::NoArgs) => Some(ReprTransparent),
149        (Some(name @ int_pat!()), ArgParser::NoArgs) => {
150            // int_pat!() should make sure it always parses
151            Some(ReprInt(int_type_of_word(name).unwrap()))
152        }
153
154        (
155            Some(
156                name @ sym::Rust
157                | name @ sym::C
158                | name @ sym::simd
159                | name @ sym::transparent
160                | name @ int_pat!(),
161            ),
162            ArgParser::NameValue(_),
163        ) => {
164            cx.emit_err(session_diagnostics::InvalidReprHintNoValue { span: param.span(), name });
165            None
166        }
167        (
168            Some(
169                name @ sym::Rust
170                | name @ sym::C
171                | name @ sym::simd
172                | name @ sym::transparent
173                | name @ int_pat!(),
174            ),
175            ArgParser::List(_),
176        ) => {
177            cx.emit_err(session_diagnostics::InvalidReprHintNoParen { span: param.span(), name });
178            None
179        }
180
181        _ => {
182            cx.emit_err(session_diagnostics::UnrecognizedReprHint { span: param.span() });
183            None
184        }
185    }
186}
187
188enum AlignKind {
189    Packed,
190    Align,
191}
192
193fn parse_repr_align<S: Stage>(
194    cx: &AcceptContext<'_, '_, S>,
195    list: &MetaItemListParser<'_>,
196    param_span: Span,
197    align_kind: AlignKind,
198) -> Option<ReprAttr> {
199    use AlignKind::*;
200
201    let Some(align) = list.single() else {
202        match align_kind {
203            Packed => {
204                cx.emit_err(session_diagnostics::IncorrectReprFormatPackedOneOrZeroArg {
205                    span: param_span,
206                });
207            }
208            Align => {
209                cx.emit_err(session_diagnostics::IncorrectReprFormatAlignOneArg {
210                    span: param_span,
211                });
212            }
213        }
214
215        return None;
216    };
217
218    let Some(lit) = align.lit() else {
219        match align_kind {
220            Packed => {
221                cx.emit_err(session_diagnostics::IncorrectReprFormatPackedExpectInteger {
222                    span: align.span(),
223                });
224            }
225            Align => {
226                cx.emit_err(session_diagnostics::IncorrectReprFormatExpectInteger {
227                    span: align.span(),
228                });
229            }
230        }
231
232        return None;
233    };
234
235    match parse_alignment(&lit.kind) {
236        Ok(literal) => Some(match align_kind {
237            AlignKind::Packed => ReprAttr::ReprPacked(literal),
238            AlignKind::Align => ReprAttr::ReprAlign(literal),
239        }),
240        Err(message) => {
241            cx.emit_err(session_diagnostics::InvalidReprGeneric {
242                span: lit.span,
243                repr_arg: match align_kind {
244                    Packed => "packed".to_string(),
245                    Align => "align".to_string(),
246                },
247                error_part: message,
248            });
249            None
250        }
251    }
252}
253
254fn parse_alignment(node: &LitKind) -> Result<Align, &'static str> {
255    if let LitKind::Int(literal, LitIntType::Unsuffixed) = node {
256        // `Align::from_bytes` accepts 0 as an input, check is_power_of_two() first
257        if literal.get().is_power_of_two() {
258            // Only possible error is larger than 2^29
259            literal
260                .get()
261                .try_into()
262                .ok()
263                .and_then(|v| Align::from_bytes(v).ok())
264                .ok_or("larger than 2^29")
265        } else {
266            Err("not a power of two")
267        }
268    } else {
269        Err("not an unsuffixed integer")
270    }
271}
272
273/// Parse #[align(N)].
274#[derive(Default)]
275pub(crate) struct AlignParser(Option<(Align, Span)>);
276
277impl AlignParser {
278    const PATH: &'static [Symbol] = &[sym::rustc_align];
279    const TEMPLATE: AttributeTemplate = template!(List: &["<alignment in bytes>"]);
280
281    fn parse<'c, S: Stage>(
282        &mut self,
283        cx: &'c mut AcceptContext<'_, '_, S>,
284        args: &'c ArgParser<'_>,
285    ) {
286        match args {
287            ArgParser::NoArgs | ArgParser::NameValue(_) => {
288                cx.expected_list(cx.attr_span);
289            }
290            ArgParser::List(list) => {
291                let Some(align) = list.single() else {
292                    cx.expected_single_argument(list.span);
293                    return;
294                };
295
296                let Some(lit) = align.lit() else {
297                    cx.emit_err(session_diagnostics::IncorrectReprFormatExpectInteger {
298                        span: align.span(),
299                    });
300
301                    return;
302                };
303
304                match parse_alignment(&lit.kind) {
305                    Ok(literal) => self.0 = Ord::max(self.0, Some((literal, cx.attr_span))),
306                    Err(message) => {
307                        cx.emit_err(session_diagnostics::InvalidAlignmentValue {
308                            span: lit.span,
309                            error_part: message,
310                        });
311                    }
312                }
313            }
314        }
315    }
316}
317
318impl<S: Stage> AttributeParser<S> for AlignParser {
319    const ATTRIBUTES: AcceptMapping<Self, S> = &[(Self::PATH, Self::TEMPLATE, Self::parse)];
320    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
321        Allow(Target::Fn),
322        Allow(Target::Method(MethodKind::Inherent)),
323        Allow(Target::Method(MethodKind::Trait { body: true })),
324        Allow(Target::Method(MethodKind::TraitImpl)),
325        Allow(Target::Method(MethodKind::Trait { body: false })),
326        Allow(Target::ForeignFn),
327    ]);
328
329    fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
330        let (align, span) = self.0?;
331        Some(AttributeKind::Align { align, span })
332    }
333}