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