Skip to main content

rustc_attr_parsing/attributes/
repr.rs

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