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