Skip to main content

rustc_attr_parsing/attributes/
repr.rs

1use rustc_abi::{Align, Size};
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 = ::rustc_feature::AttributeTemplate {
    word: false,
    list: Some(&["C", "Rust", "transparent", "align(...)", "packed(...)",
                    "<integer type>"]),
    one_of: &[],
    name_value_str: None,
    docs: Some("https://doc.rust-lang.org/reference/type-layout.html#representations"),
}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            let attr_span = cx.attr_span;
37            cx.adcx().expected_list(attr_span, args);
38            return reprs;
39        };
40
41        if list.is_empty() {
42            let attr_span = cx.attr_span;
43            cx.adcx().warn_empty_attribute(attr_span);
44            return reprs;
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    //FIXME Still checked fully in `check_attr.rs`
62    //This one is slightly more complicated because the allowed targets depend on the arguments
63    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
64}
65
66macro_rules! int_pat {
67    () => {
68        sym::i8
69            | sym::u8
70            | sym::i16
71            | sym::u16
72            | sym::i32
73            | sym::u32
74            | sym::i64
75            | sym::u64
76            | sym::i128
77            | sym::u128
78            | sym::isize
79            | sym::usize
80    };
81}
82
83fn int_type_of_word(s: Symbol) -> Option<IntType> {
84    use IntType::*;
85
86    match s {
87        sym::i8 => Some(SignedInt(IntTy::I8)),
88        sym::u8 => Some(UnsignedInt(UintTy::U8)),
89        sym::i16 => Some(SignedInt(IntTy::I16)),
90        sym::u16 => Some(UnsignedInt(UintTy::U16)),
91        sym::i32 => Some(SignedInt(IntTy::I32)),
92        sym::u32 => Some(UnsignedInt(UintTy::U32)),
93        sym::i64 => Some(SignedInt(IntTy::I64)),
94        sym::u64 => Some(UnsignedInt(UintTy::U64)),
95        sym::i128 => Some(SignedInt(IntTy::I128)),
96        sym::u128 => Some(UnsignedInt(UintTy::U128)),
97        sym::isize => Some(SignedInt(IntTy::Isize)),
98        sym::usize => Some(UnsignedInt(UintTy::Usize)),
99        _ => None,
100    }
101}
102
103fn parse_repr<S: Stage>(cx: &AcceptContext<'_, '_, S>, param: &MetaItemParser) -> Option<ReprAttr> {
104    use ReprAttr::*;
105
106    // FIXME(jdonszelmann): invert the parsing here to match on the word first and then the
107    // structure.
108    let (name, ident_span) = if let Some(ident) = param.path().word() {
109        (Some(ident.name), ident.span)
110    } else {
111        (None, DUMMY_SP)
112    };
113
114    let args = param.args();
115
116    match (name, args) {
117        (Some(sym::align), ArgParser::NoArgs) => {
118            cx.emit_err(session_diagnostics::InvalidReprAlignNeedArg { span: ident_span });
119            None
120        }
121        (Some(sym::align), ArgParser::List(l)) => {
122            parse_repr_align(cx, l, param.span(), AlignKind::Align)
123        }
124
125        (Some(sym::packed), ArgParser::NoArgs) => Some(ReprPacked(Align::ONE)),
126        (Some(sym::packed), ArgParser::List(l)) => {
127            parse_repr_align(cx, l, param.span(), AlignKind::Packed)
128        }
129
130        (Some(name @ sym::align | name @ sym::packed), ArgParser::NameValue(l)) => {
131            cx.emit_err(session_diagnostics::IncorrectReprFormatGeneric {
132                span: param.span(),
133                // FIXME(jdonszelmann) can just be a string in the diag type
134                repr_arg: name,
135                cause: IncorrectReprFormatGenericCause::from_lit_kind(
136                    param.span(),
137                    &l.value_as_lit().kind,
138                    name,
139                ),
140            });
141            None
142        }
143
144        (Some(sym::Rust), ArgParser::NoArgs) => Some(ReprRust),
145        (Some(sym::C), ArgParser::NoArgs) => Some(ReprC),
146        (Some(sym::simd), ArgParser::NoArgs) => Some(ReprSimd),
147        (Some(sym::transparent), ArgParser::NoArgs) => Some(ReprTransparent),
148        (Some(name @ sym::i8 | sym::u8 | sym::i16 | sym::u16 | sym::i32 | sym::u32 | sym::i64 |
sym::u64 | sym::i128 | sym::u128 | sym::isize | sym::usizeint_pat!()), ArgParser::NoArgs) => {
149            // int_pat!() should make sure it always parses
150            Some(ReprInt(int_type_of_word(name).unwrap()))
151        }
152
153        (
154            Some(
155                name @ sym::Rust
156                | name @ sym::C
157                | name @ sym::simd
158                | name @ sym::transparent
159                | name @ sym::i8 | sym::u8 | sym::i16 | sym::u16 | sym::i32 | sym::u32 | sym::i64 |
sym::u64 | sym::i128 | sym::u128 | sym::isize | sym::usizeint_pat!(),
160            ),
161            ArgParser::NameValue(_),
162        ) => {
163            cx.emit_err(session_diagnostics::InvalidReprHintNoValue { span: param.span(), name });
164            None
165        }
166        (
167            Some(
168                name @ sym::Rust
169                | name @ sym::C
170                | name @ sym::simd
171                | name @ sym::transparent
172                | name @ sym::i8 | sym::u8 | sym::i16 | sym::u16 | sym::i32 | sym::u32 | sym::i64 |
sym::u64 | sym::i128 | sym::u128 | sym::isize | sym::usizeint_pat!(),
173            ),
174            ArgParser::List(_),
175        ) => {
176            cx.emit_err(session_diagnostics::InvalidReprHintNoParen { span: param.span(), name });
177            None
178        }
179
180        _ => {
181            cx.emit_err(session_diagnostics::UnrecognizedReprHint { span: param.span() });
182            None
183        }
184    }
185}
186
187enum AlignKind {
188    Packed,
189    Align,
190}
191
192fn parse_repr_align<S: Stage>(
193    cx: &AcceptContext<'_, '_, S>,
194    list: &MetaItemListParser,
195    param_span: Span,
196    align_kind: AlignKind,
197) -> Option<ReprAttr> {
198    use AlignKind::*;
199
200    let Some(align) = list.single() else {
201        match align_kind {
202            Packed => {
203                cx.emit_err(session_diagnostics::IncorrectReprFormatPackedOneOrZeroArg {
204                    span: param_span,
205                });
206            }
207            Align => {
208                cx.emit_err(session_diagnostics::IncorrectReprFormatAlignOneArg {
209                    span: param_span,
210                });
211            }
212        }
213
214        return None;
215    };
216
217    let Some(lit) = align.lit() else {
218        match align_kind {
219            Packed => {
220                cx.emit_err(session_diagnostics::IncorrectReprFormatPackedExpectInteger {
221                    span: align.span(),
222                });
223            }
224            Align => {
225                cx.emit_err(session_diagnostics::IncorrectReprFormatExpectInteger {
226                    span: align.span(),
227                });
228            }
229        }
230
231        return None;
232    };
233
234    match parse_alignment(&lit.kind, cx) {
235        Ok(literal) => Some(match align_kind {
236            AlignKind::Packed => ReprAttr::ReprPacked(literal),
237            AlignKind::Align => ReprAttr::ReprAlign(literal),
238        }),
239        Err(message) => {
240            cx.emit_err(session_diagnostics::InvalidReprGeneric {
241                span: lit.span,
242                repr_arg: match align_kind {
243                    Packed => "packed".to_string(),
244                    Align => "align".to_string(),
245                },
246                error_part: message,
247            });
248            None
249        }
250    }
251}
252
253fn parse_alignment<S: Stage>(
254    node: &LitKind,
255    cx: &AcceptContext<'_, '_, S>,
256) -> Result<Align, String> {
257    let LitKind::Int(literal, LitIntType::Unsuffixed) = node else {
258        return Err("not an unsuffixed integer".to_string());
259    };
260
261    // `Align::from_bytes` accepts 0 as a valid input,
262    // so we check if its a power of two first
263    if !literal.get().is_power_of_two() {
264        return Err("not a power of two".to_string());
265    }
266    // lit must be < 2^29
267    let align = literal
268        .get()
269        .try_into()
270        .ok()
271        .and_then(|a| Align::from_bytes(a).ok())
272        .ok_or("larger than 2^29".to_string())?;
273
274    // alignment must not be larger than the pointer width (`isize::MAX`)
275    let max = Size::from_bits(cx.sess.target.pointer_width).signed_int_max() as u64;
276    if align.bytes() > max {
277        return Err(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("alignment larger than `isize::MAX` bytes ({0} for the current target)",
                max))
    })format!(
278            "alignment larger than `isize::MAX` bytes ({max} for the current target)"
279        ));
280    }
281    Ok(align)
282}
283
284/// Parse #[align(N)].
285#[derive(#[automatically_derived]
impl ::core::default::Default for RustcAlignParser {
    #[inline]
    fn default() -> RustcAlignParser {
        RustcAlignParser(::core::default::Default::default())
    }
}Default)]
286pub(crate) struct RustcAlignParser(Option<(Align, Span)>);
287
288impl RustcAlignParser {
289    const PATH: &[Symbol] = &[sym::rustc_align];
290    const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
    word: false,
    list: Some(&["<alignment in bytes>"]),
    one_of: &[],
    name_value_str: None,
    docs: None,
}template!(List: &["<alignment in bytes>"]);
291
292    fn parse<S: Stage>(&mut self, cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) {
293        match args {
294            ArgParser::NoArgs | ArgParser::NameValue(_) => {
295                let attr_span = cx.attr_span;
296                cx.adcx().expected_list(attr_span, args);
297            }
298            ArgParser::List(list) => {
299                let Some(align) = list.single() else {
300                    cx.adcx().expected_single_argument(list.span);
301                    return;
302                };
303
304                let Some(lit) = align.lit() else {
305                    cx.emit_err(session_diagnostics::IncorrectReprFormatExpectInteger {
306                        span: align.span(),
307                    });
308
309                    return;
310                };
311
312                match parse_alignment(&lit.kind, cx) {
313                    Ok(literal) => self.0 = Ord::max(self.0, Some((literal, cx.attr_span))),
314                    Err(message) => {
315                        cx.emit_err(session_diagnostics::InvalidAlignmentValue {
316                            span: lit.span,
317                            error_part: message,
318                        });
319                    }
320                }
321            }
322        }
323    }
324}
325
326impl<S: Stage> AttributeParser<S> for RustcAlignParser {
327    const ATTRIBUTES: AcceptMapping<Self, S> = &[(Self::PATH, Self::TEMPLATE, Self::parse)];
328    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
329        Allow(Target::Fn),
330        Allow(Target::Method(MethodKind::Inherent)),
331        Allow(Target::Method(MethodKind::Trait { body: true })),
332        Allow(Target::Method(MethodKind::TraitImpl)),
333        Allow(Target::Method(MethodKind::Trait { body: false })), // `#[align]` is inherited from trait methods
334        Allow(Target::ForeignFn),
335    ]);
336
337    fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
338        let (align, span) = self.0?;
339        Some(AttributeKind::RustcAlign { align, span })
340    }
341}
342
343#[derive(#[automatically_derived]
impl ::core::default::Default for RustcAlignStaticParser {
    #[inline]
    fn default() -> RustcAlignStaticParser {
        RustcAlignStaticParser(::core::default::Default::default())
    }
}Default)]
344pub(crate) struct RustcAlignStaticParser(RustcAlignParser);
345
346impl RustcAlignStaticParser {
347    const PATH: &[Symbol] = &[sym::rustc_align_static];
348    const TEMPLATE: AttributeTemplate = RustcAlignParser::TEMPLATE;
349
350    fn parse<S: Stage>(&mut self, cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) {
351        self.0.parse(cx, args)
352    }
353}
354
355impl<S: Stage> AttributeParser<S> for RustcAlignStaticParser {
356    const ATTRIBUTES: AcceptMapping<Self, S> = &[(Self::PATH, Self::TEMPLATE, Self::parse)];
357    const ALLOWED_TARGETS: AllowedTargets =
358        AllowedTargets::AllowList(&[Allow(Target::Static), Allow(Target::ForeignStatic)]);
359
360    fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
361        let (align, span) = self.0.0?;
362        Some(AttributeKind::RustcAlign { align, span })
363    }
364}