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