Skip to main content

rustc_attr_parsing/attributes/
repr.rs

1use rustc_abi::{Align, Size};
2use rustc_ast::{IntTy, LitIntType, LitKind, UintTy};
3use rustc_feature::AttributeStability;
4use rustc_hir::attrs::IntType::{SignedInt, UnsignedInt};
5use rustc_hir::attrs::ReprAttr;
6use rustc_session::diagnostics::feature_err;
7
8use super::prelude::*;
9use crate::session_diagnostics;
10
11/// Parse #[repr(...)] forms.
12///
13/// Valid repr contents:
14/// * any of the primitive integral type names to specify enum discriminant type
15/// * `Rust`, to use the default `Rust` layout of the type
16/// * `C`, to use the same layout for the type that C would use
17/// * `align(...)`, to change the alignment requirements of the type
18/// * `packed`, to remove padding
19/// * `transparent`, to delegate representation concerns to the only non-ZST field.
20pub(crate) struct ReprParser;
21
22impl CombineAttributeParser for ReprParser {
23    type Item = (ReprAttr, Span);
24    const PATH: &[Symbol] = &[sym::repr];
25    const CONVERT: ConvertFn<Self::Item> =
26        |items, first_span| AttributeKind::Repr { reprs: items, first_span };
27    const TEMPLATE: AttributeTemplate = crate::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!(
28        List: &["C", "Rust", "transparent", "align(...)", "packed(...)", "<integer type>"],
29        "https://doc.rust-lang.org/reference/type-layout.html#representations"
30    );
31
32    fn extend(
33        cx: &mut AcceptContext<'_, '_>,
34        args: &ArgParser,
35    ) -> impl IntoIterator<Item = Self::Item> {
36        let Some(list) = cx.expect_list(args, cx.attr_span) else {
37            return ::alloc::vec::Vec::new()vec![];
38        };
39
40        if list.is_empty() {
41            cx.check_target(
42                "()",
43                &AllowedTargets::AllowList(&[
44                    Allow(Target::Struct),
45                    Allow(Target::Enum),
46                    Allow(Target::Union),
47                    Warn(Target::MacroCall),
48                ]),
49            );
50
51            let attr_span = cx.attr_span;
52            cx.adcx().warn_empty_attribute(attr_span);
53            return ::alloc::vec::Vec::new()vec![];
54        }
55
56        let mut reprs = Vec::new();
57        for param in list.mixed() {
58            let Some(item) = param.meta_item() else {
59                cx.adcx().expected_identifier(param.span());
60                continue;
61            };
62            reprs.extend(parse_repr(cx, &item).map(|r| (r, param.span())));
63        }
64        reprs
65    }
66
67    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::ManuallyChecked;
68    const STABILITY: AttributeStability = AttributeStability::Stable;
69}
70
71fn parse_repr(cx: &mut AcceptContext<'_, '_>, param: &MetaItemParser) -> Option<ReprAttr> {
72    use ReprAttr::*;
73
74    macro_rules! repr_int {
75        ($arg: ident, $constructor: expr) => {{
76            cx.check_target(
77                concat!("(", stringify!($arg), ")"),
78                &AllowedTargets::AllowList(&[Allow(Target::Enum), Warn(Target::MacroCall)]),
79            );
80            cx.expect_no_args(param.args())?;
81            Some($constructor)
82        }};
83    }
84
85    match param.path().word_sym() {
86        Some(sym::align) => {
87            cx.check_target(
88                "(align(...))",
89                &AllowedTargets::AllowList(&[
90                    Allow(Target::Struct),
91                    Allow(Target::Enum),
92                    Allow(Target::Union),
93                    Warn(Target::MacroCall),
94                ]),
95            );
96            let l = cx.expect_list(param.args(), param.span())?;
97            parse_repr_align(cx, l, AlignKind::Align)
98        }
99        Some(sym::packed) => {
100            cx.check_target(
101                "(packed)",
102                &AllowedTargets::AllowList(&[
103                    Allow(Target::Struct),
104                    Allow(Target::Union),
105                    Warn(Target::MacroCall),
106                ]),
107            );
108            match param.args() {
109                ArgParser::NoArgs => Some(ReprPacked(Align::ONE)),
110                ArgParser::List(l) => parse_repr_align(cx, l, AlignKind::Packed),
111                ArgParser::NameValue(_) => {
112                    cx.adcx().expected_list_or_no_args(param.span());
113                    None
114                }
115            }
116        }
117
118        Some(sym::Rust) => {
119            cx.check_target(
120                "(Rust)",
121                &AllowedTargets::AllowList(&[
122                    Allow(Target::Struct),
123                    Allow(Target::Enum),
124                    Allow(Target::Union),
125                    Warn(Target::MacroCall),
126                ]),
127            );
128            cx.expect_no_args(param.args())?;
129            Some(ReprRust)
130        }
131        Some(sym::C) => {
132            cx.check_target(
133                "(C)",
134                &AllowedTargets::AllowList(&[
135                    Allow(Target::Struct),
136                    Allow(Target::Enum),
137                    Allow(Target::Union),
138                    Warn(Target::MacroCall),
139                ]),
140            );
141            cx.expect_no_args(param.args())?;
142            Some(ReprC)
143        }
144        Some(sym::simd) => {
145            if cx.features.is_some_and(|feats| !feats.repr_simd()) {
146                feature_err(
147                    &cx.sess(),
148                    sym::repr_simd,
149                    param.span(),
150                    "SIMD types are experimental and possibly buggy",
151                )
152                .emit();
153            }
154            cx.check_target("(simd)", &AllowedTargets::AllowList(&[Allow(Target::Struct)]));
155            cx.expect_no_args(param.args())?;
156            Some(ReprSimd)
157        }
158        Some(sym::transparent) => {
159            cx.check_target(
160                "(transparent)",
161                &AllowedTargets::AllowList(&[
162                    Allow(Target::Struct),
163                    Allow(Target::Enum),
164                    Allow(Target::Union), // Feature gated in `rustc_hir_analysis`
165                    Warn(Target::MacroCall),
166                ]),
167            );
168            cx.expect_no_args(param.args())?;
169            Some(ReprTransparent)
170        }
171
172        Some(sym::i8) => {
    cx.check_target("(i8)",
        &AllowedTargets::AllowList(&[Allow(Target::Enum),
                            Warn(Target::MacroCall)]));
    cx.expect_no_args(param.args())?;
    Some(ReprInt(SignedInt(IntTy::I8)))
}repr_int!(i8, ReprInt(SignedInt(IntTy::I8))),
173        Some(sym::u8) => {
    cx.check_target("(u8)",
        &AllowedTargets::AllowList(&[Allow(Target::Enum),
                            Warn(Target::MacroCall)]));
    cx.expect_no_args(param.args())?;
    Some(ReprInt(UnsignedInt(UintTy::U8)))
}repr_int!(u8, ReprInt(UnsignedInt(UintTy::U8))),
174        Some(sym::i16) => {
    cx.check_target("(i16)",
        &AllowedTargets::AllowList(&[Allow(Target::Enum),
                            Warn(Target::MacroCall)]));
    cx.expect_no_args(param.args())?;
    Some(ReprInt(SignedInt(IntTy::I16)))
}repr_int!(i16, ReprInt(SignedInt(IntTy::I16))),
175        Some(sym::u16) => {
    cx.check_target("(u16)",
        &AllowedTargets::AllowList(&[Allow(Target::Enum),
                            Warn(Target::MacroCall)]));
    cx.expect_no_args(param.args())?;
    Some(ReprInt(UnsignedInt(UintTy::U16)))
}repr_int!(u16, ReprInt(UnsignedInt(UintTy::U16))),
176        Some(sym::i32) => {
    cx.check_target("(i32)",
        &AllowedTargets::AllowList(&[Allow(Target::Enum),
                            Warn(Target::MacroCall)]));
    cx.expect_no_args(param.args())?;
    Some(ReprInt(SignedInt(IntTy::I32)))
}repr_int!(i32, ReprInt(SignedInt(IntTy::I32))),
177        Some(sym::u32) => {
    cx.check_target("(u32)",
        &AllowedTargets::AllowList(&[Allow(Target::Enum),
                            Warn(Target::MacroCall)]));
    cx.expect_no_args(param.args())?;
    Some(ReprInt(UnsignedInt(UintTy::U32)))
}repr_int!(u32, ReprInt(UnsignedInt(UintTy::U32))),
178        Some(sym::i64) => {
    cx.check_target("(i64)",
        &AllowedTargets::AllowList(&[Allow(Target::Enum),
                            Warn(Target::MacroCall)]));
    cx.expect_no_args(param.args())?;
    Some(ReprInt(SignedInt(IntTy::I64)))
}repr_int!(i64, ReprInt(SignedInt(IntTy::I64))),
179        Some(sym::u64) => {
    cx.check_target("(u64)",
        &AllowedTargets::AllowList(&[Allow(Target::Enum),
                            Warn(Target::MacroCall)]));
    cx.expect_no_args(param.args())?;
    Some(ReprInt(UnsignedInt(UintTy::U64)))
}repr_int!(u64, ReprInt(UnsignedInt(UintTy::U64))),
180        Some(sym::i128) => {
    cx.check_target("(i128)",
        &AllowedTargets::AllowList(&[Allow(Target::Enum),
                            Warn(Target::MacroCall)]));
    cx.expect_no_args(param.args())?;
    Some(ReprInt(SignedInt(IntTy::I128)))
}repr_int!(i128, ReprInt(SignedInt(IntTy::I128))),
181        Some(sym::u128) => {
    cx.check_target("(u128)",
        &AllowedTargets::AllowList(&[Allow(Target::Enum),
                            Warn(Target::MacroCall)]));
    cx.expect_no_args(param.args())?;
    Some(ReprInt(UnsignedInt(UintTy::U128)))
}repr_int!(u128, ReprInt(UnsignedInt(UintTy::U128))),
182        Some(sym::isize) => {
    cx.check_target("(isize)",
        &AllowedTargets::AllowList(&[Allow(Target::Enum),
                            Warn(Target::MacroCall)]));
    cx.expect_no_args(param.args())?;
    Some(ReprInt(SignedInt(IntTy::Isize)))
}repr_int!(isize, ReprInt(SignedInt(IntTy::Isize))),
183        Some(sym::usize) => {
    cx.check_target("(usize)",
        &AllowedTargets::AllowList(&[Allow(Target::Enum),
                            Warn(Target::MacroCall)]));
    cx.expect_no_args(param.args())?;
    Some(ReprInt(UnsignedInt(UintTy::Usize)))
}repr_int!(usize, ReprInt(UnsignedInt(UintTy::Usize))),
184        _ => {
185            cx.adcx().expected_specific_argument(
186                param.span(),
187                &[
188                    sym::align,
189                    sym::packed,
190                    sym::Rust,
191                    sym::C,
192                    sym::simd,
193                    sym::transparent,
194                    sym::i8,
195                    sym::u8,
196                    sym::i16,
197                    sym::u16,
198                    sym::i32,
199                    sym::u32,
200                    sym::i64,
201                    sym::u64,
202                    sym::i128,
203                    sym::u128,
204                    sym::isize,
205                    sym::usize,
206                ],
207            );
208            None
209        }
210    }
211}
212
213enum AlignKind {
214    Packed,
215    Align,
216}
217
218fn parse_repr_align(
219    cx: &mut AcceptContext<'_, '_>,
220    list: &MetaItemListParser,
221    align_kind: AlignKind,
222) -> Option<ReprAttr> {
223    let Some(align) = list.as_single() else {
224        cx.adcx().expected_single_argument(list.span, list.len());
225        return None;
226    };
227
228    let Some(lit) = align.as_lit() else {
229        cx.adcx().expected_integer_literal(align.span());
230        return None;
231    };
232
233    match parse_alignment(&lit.kind, cx) {
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::InvalidAlignmentValue {
240                span: lit.span,
241                error_part: message,
242            });
243            None
244        }
245    }
246}
247
248fn parse_alignment(node: &LitKind, cx: &AcceptContext<'_, '_>) -> Result<Align, String> {
249    let LitKind::Int(literal, LitIntType::Unsuffixed) = node else {
250        return Err("not an unsuffixed integer".to_string());
251    };
252
253    // `Align::from_bytes` accepts 0 as a valid input,
254    // so we check if its a power of two first
255    if !literal.get().is_power_of_two() {
256        return Err("not a power of two".to_string());
257    }
258    // lit must be < 2^29
259    let align = literal
260        .get()
261        .try_into()
262        .ok()
263        .and_then(|a| Align::from_bytes(a).ok())
264        .ok_or("larger than 2^29".to_string())?;
265
266    // alignment must not be larger than the pointer width (`isize::MAX`)
267    let max = Size::from_bits(cx.sess.target.pointer_width).signed_int_max() as u64;
268    if align.bytes() > max {
269        return Err(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("alignment larger than `isize::MAX` bytes ({0} for the current target)",
                max))
    })format!(
270            "alignment larger than `isize::MAX` bytes ({max} for the current target)"
271        ));
272    }
273    Ok(align)
274}
275
276/// Parse #[align(N)].
277#[derive(#[automatically_derived]
impl ::core::default::Default for RustcAlignParser {
    #[inline]
    fn default() -> RustcAlignParser {
        RustcAlignParser(::core::default::Default::default())
    }
}Default)]
278pub(crate) struct RustcAlignParser(Option<(Align, Span)>);
279
280impl RustcAlignParser {
281    const PATH: &[Symbol] = &[sym::rustc_align];
282    const TEMPLATE: AttributeTemplate = crate::AttributeTemplate {
    word: false,
    list: Some(&["<alignment in bytes>"]),
    one_of: &[],
    name_value_str: None,
    docs: None,
}template!(List: &["<alignment in bytes>"]);
283
284    fn parse(&mut self, cx: &mut AcceptContext<'_, '_>, args: &ArgParser) {
285        let Some(list) = cx.expect_list(args, cx.attr_span) else {
286            return;
287        };
288
289        let Some(align) = cx.expect_single(list) else {
290            return;
291        };
292
293        let Some(lit) = align.as_lit() else {
294            cx.adcx().expected_integer_literal(align.span());
295            return;
296        };
297
298        match parse_alignment(&lit.kind, cx) {
299            Ok(literal) => self.0 = Ord::max(self.0, Some((literal, cx.attr_span))),
300            Err(message) => {
301                cx.emit_err(session_diagnostics::InvalidAlignmentValue {
302                    span: lit.span,
303                    error_part: message,
304                });
305            }
306        }
307    }
308}
309
310impl AttributeParser for RustcAlignParser {
311    const ATTRIBUTES: AcceptMapping<Self> =
312        &[(Self::PATH, Self::TEMPLATE, AttributeStability::Unstable {
    gate_name: rustc_span::sym::fn_align,
    gate_check: rustc_feature::Features::fn_align,
    notes: &[],
}unstable!(fn_align), 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<'_, '_>) -> Option<AttributeKind> {
323        let (align, span) = self.0?;
324        Some(AttributeKind::RustcAlign { align, span })
325    }
326}
327
328#[derive(#[automatically_derived]
impl ::core::default::Default for RustcAlignStaticParser {
    #[inline]
    fn default() -> RustcAlignStaticParser {
        RustcAlignStaticParser(::core::default::Default::default())
    }
}Default)]
329pub(crate) struct RustcAlignStaticParser(RustcAlignParser);
330
331impl RustcAlignStaticParser {
332    const PATH: &[Symbol] = &[sym::rustc_align_static];
333    const TEMPLATE: AttributeTemplate = RustcAlignParser::TEMPLATE;
334
335    fn parse(&mut self, cx: &mut AcceptContext<'_, '_>, args: &ArgParser) {
336        self.0.parse(cx, args)
337    }
338}
339
340impl AttributeParser for RustcAlignStaticParser {
341    const ATTRIBUTES: AcceptMapping<Self> =
342        &[(Self::PATH, Self::TEMPLATE, AttributeStability::Unstable {
    gate_name: rustc_span::sym::static_align,
    gate_check: rustc_feature::Features::static_align,
    notes: &[],
}unstable!(static_align), Self::parse)];
343    const ALLOWED_TARGETS: AllowedTargets<'_> =
344        AllowedTargets::AllowList(&[Allow(Target::Static), Allow(Target::ForeignStatic)]);
345
346    fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {
347        let (align, span) = self.0.0?;
348        Some(AttributeKind::RustcAlign { align, span })
349    }
350}