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::{SignedInt, UnsignedInt};
4use rustc_hir::attrs::ReprAttr;
5
6use super::prelude::*;
7use crate::session_diagnostics;
8
9/// Parse #[repr(...)] forms.
10///
11/// Valid repr contents:
12/// * any of the primitive integral type names to specify enum discriminant type
13/// * `Rust`, to use the default `Rust` layout of the type
14/// * `C`, to use the same layout for the type that C would use
15/// * `align(...)`, to change the alignment requirements of the type
16/// * `packed`, to remove padding
17/// * `transparent`, to delegate representation concerns to the only non-ZST field.
18pub(crate) struct ReprParser;
19
20impl CombineAttributeParser for ReprParser {
21    type Item = (ReprAttr, Span);
22    const PATH: &[Symbol] = &[sym::repr];
23    const CONVERT: ConvertFn<Self::Item> =
24        |items, first_span| AttributeKind::Repr { reprs: items, first_span };
25    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!(
26        List: &["C", "Rust", "transparent", "align(...)", "packed(...)", "<integer type>"],
27        "https://doc.rust-lang.org/reference/type-layout.html#representations"
28    );
29
30    fn extend(
31        cx: &mut AcceptContext<'_, '_>,
32        args: &ArgParser,
33    ) -> impl IntoIterator<Item = Self::Item> {
34        let Some(list) = cx.expect_list(args, cx.attr_span) else {
35            return ::alloc::vec::Vec::new()vec![];
36        };
37
38        if list.is_empty() {
39            let attr_span = cx.attr_span;
40            cx.adcx().warn_empty_attribute(attr_span);
41            return ::alloc::vec::Vec::new()vec![];
42        }
43
44        let mut reprs = Vec::new();
45        for param in list.mixed() {
46            let Some(item) = param.meta_item() else {
47                cx.adcx().expected_identifier(param.span());
48                continue;
49            };
50            reprs.extend(parse_repr(cx, &item).map(|r| (r, param.span())));
51        }
52        reprs
53    }
54
55    //FIXME Still checked fully in `check_attr.rs`
56    //This one is slightly more complicated because the allowed targets depend on the arguments
57    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
58}
59
60fn parse_repr(cx: &mut AcceptContext<'_, '_>, param: &MetaItemParser) -> Option<ReprAttr> {
61    use ReprAttr::*;
62
63    macro_rules! no_args {
64        ($constructor: expr) => {{
65            cx.expect_no_args(param.args())?;
66            Some($constructor)
67        }};
68    }
69
70    match param.path().word_sym() {
71        Some(sym::align) => {
72            let l = cx.expect_list(param.args(), param.span())?;
73            parse_repr_align(cx, l, AlignKind::Align)
74        }
75        Some(sym::packed) => match param.args() {
76            ArgParser::NoArgs => Some(ReprPacked(Align::ONE)),
77            ArgParser::List(l) => parse_repr_align(cx, l, AlignKind::Packed),
78            ArgParser::NameValue(_) => {
79                cx.adcx().expected_list_or_no_args(param.span());
80                None
81            }
82        },
83        Some(sym::Rust) => { cx.expect_no_args(param.args())?; Some(ReprRust) }no_args!(ReprRust),
84        Some(sym::C) => { cx.expect_no_args(param.args())?; Some(ReprC) }no_args!(ReprC),
85        Some(sym::simd) => { cx.expect_no_args(param.args())?; Some(ReprSimd) }no_args!(ReprSimd),
86        Some(sym::transparent) => { cx.expect_no_args(param.args())?; Some(ReprTransparent) }no_args!(ReprTransparent),
87        Some(sym::i8) => { cx.expect_no_args(param.args())?; Some(ReprInt(SignedInt(IntTy::I8))) }no_args!(ReprInt(SignedInt(IntTy::I8))),
88        Some(sym::u8) => { cx.expect_no_args(param.args())?; Some(ReprInt(UnsignedInt(UintTy::U8))) }no_args!(ReprInt(UnsignedInt(UintTy::U8))),
89        Some(sym::i16) => { cx.expect_no_args(param.args())?; Some(ReprInt(SignedInt(IntTy::I16))) }no_args!(ReprInt(SignedInt(IntTy::I16))),
90        Some(sym::u16) => { cx.expect_no_args(param.args())?; Some(ReprInt(UnsignedInt(UintTy::U16))) }no_args!(ReprInt(UnsignedInt(UintTy::U16))),
91        Some(sym::i32) => { cx.expect_no_args(param.args())?; Some(ReprInt(SignedInt(IntTy::I32))) }no_args!(ReprInt(SignedInt(IntTy::I32))),
92        Some(sym::u32) => { cx.expect_no_args(param.args())?; Some(ReprInt(UnsignedInt(UintTy::U32))) }no_args!(ReprInt(UnsignedInt(UintTy::U32))),
93        Some(sym::i64) => { cx.expect_no_args(param.args())?; Some(ReprInt(SignedInt(IntTy::I64))) }no_args!(ReprInt(SignedInt(IntTy::I64))),
94        Some(sym::u64) => { cx.expect_no_args(param.args())?; Some(ReprInt(UnsignedInt(UintTy::U64))) }no_args!(ReprInt(UnsignedInt(UintTy::U64))),
95        Some(sym::i128) => { cx.expect_no_args(param.args())?; Some(ReprInt(SignedInt(IntTy::I128))) }no_args!(ReprInt(SignedInt(IntTy::I128))),
96        Some(sym::u128) => { cx.expect_no_args(param.args())?; Some(ReprInt(UnsignedInt(UintTy::U128))) }no_args!(ReprInt(UnsignedInt(UintTy::U128))),
97        Some(sym::isize) => { cx.expect_no_args(param.args())?; Some(ReprInt(SignedInt(IntTy::Isize))) }no_args!(ReprInt(SignedInt(IntTy::Isize))),
98        Some(sym::usize) => {
    cx.expect_no_args(param.args())?;
    Some(ReprInt(UnsignedInt(UintTy::Usize)))
}no_args!(ReprInt(UnsignedInt(UintTy::Usize))),
99        _ => {
100            cx.adcx().expected_specific_argument(
101                param.span(),
102                &[
103                    sym::align,
104                    sym::packed,
105                    sym::Rust,
106                    sym::C,
107                    sym::simd,
108                    sym::transparent,
109                    sym::i8,
110                    sym::u8,
111                    sym::i16,
112                    sym::u16,
113                    sym::i32,
114                    sym::u32,
115                    sym::i64,
116                    sym::u64,
117                    sym::i128,
118                    sym::u128,
119                    sym::isize,
120                    sym::usize,
121                ],
122            );
123            None
124        }
125    }
126}
127
128enum AlignKind {
129    Packed,
130    Align,
131}
132
133fn parse_repr_align(
134    cx: &mut AcceptContext<'_, '_>,
135    list: &MetaItemListParser,
136    align_kind: AlignKind,
137) -> Option<ReprAttr> {
138    let Some(align) = list.as_single() else {
139        cx.adcx().expected_single_argument(list.span, list.len());
140        return None;
141    };
142
143    let Some(lit) = align.as_lit() else {
144        cx.adcx().expected_integer_literal(align.span());
145        return None;
146    };
147
148    match parse_alignment(&lit.kind, cx) {
149        Ok(literal) => Some(match align_kind {
150            AlignKind::Packed => ReprAttr::ReprPacked(literal),
151            AlignKind::Align => ReprAttr::ReprAlign(literal),
152        }),
153        Err(message) => {
154            cx.emit_err(session_diagnostics::InvalidAlignmentValue {
155                span: lit.span,
156                error_part: message,
157            });
158            None
159        }
160    }
161}
162
163fn parse_alignment(node: &LitKind, cx: &AcceptContext<'_, '_>) -> Result<Align, String> {
164    let LitKind::Int(literal, LitIntType::Unsuffixed) = node else {
165        return Err("not an unsuffixed integer".to_string());
166    };
167
168    // `Align::from_bytes` accepts 0 as a valid input,
169    // so we check if its a power of two first
170    if !literal.get().is_power_of_two() {
171        return Err("not a power of two".to_string());
172    }
173    // lit must be < 2^29
174    let align = literal
175        .get()
176        .try_into()
177        .ok()
178        .and_then(|a| Align::from_bytes(a).ok())
179        .ok_or("larger than 2^29".to_string())?;
180
181    // alignment must not be larger than the pointer width (`isize::MAX`)
182    let max = Size::from_bits(cx.sess.target.pointer_width).signed_int_max() as u64;
183    if align.bytes() > max {
184        return Err(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("alignment larger than `isize::MAX` bytes ({0} for the current target)",
                max))
    })format!(
185            "alignment larger than `isize::MAX` bytes ({max} for the current target)"
186        ));
187    }
188    Ok(align)
189}
190
191/// Parse #[align(N)].
192#[derive(#[automatically_derived]
impl ::core::default::Default for RustcAlignParser {
    #[inline]
    fn default() -> RustcAlignParser {
        RustcAlignParser(::core::default::Default::default())
    }
}Default)]
193pub(crate) struct RustcAlignParser(Option<(Align, Span)>);
194
195impl RustcAlignParser {
196    const PATH: &[Symbol] = &[sym::rustc_align];
197    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>"]);
198
199    fn parse(&mut self, cx: &mut AcceptContext<'_, '_>, args: &ArgParser) {
200        let Some(list) = cx.expect_list(args, cx.attr_span) else {
201            return;
202        };
203
204        let Some(align) = cx.expect_single(list) else {
205            return;
206        };
207
208        let Some(lit) = align.as_lit() else {
209            cx.adcx().expected_integer_literal(align.span());
210            return;
211        };
212
213        match parse_alignment(&lit.kind, cx) {
214            Ok(literal) => self.0 = Ord::max(self.0, Some((literal, cx.attr_span))),
215            Err(message) => {
216                cx.emit_err(session_diagnostics::InvalidAlignmentValue {
217                    span: lit.span,
218                    error_part: message,
219                });
220            }
221        }
222    }
223}
224
225impl AttributeParser for RustcAlignParser {
226    const ATTRIBUTES: AcceptMapping<Self> = &[(Self::PATH, Self::TEMPLATE, Self::parse)];
227    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
228        Allow(Target::Fn),
229        Allow(Target::Method(MethodKind::Inherent)),
230        Allow(Target::Method(MethodKind::Trait { body: true })),
231        Allow(Target::Method(MethodKind::TraitImpl)),
232        Allow(Target::Method(MethodKind::Trait { body: false })), // `#[align]` is inherited from trait methods
233        Allow(Target::ForeignFn),
234    ]);
235
236    fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {
237        let (align, span) = self.0?;
238        Some(AttributeKind::RustcAlign { align, span })
239    }
240}
241
242#[derive(#[automatically_derived]
impl ::core::default::Default for RustcAlignStaticParser {
    #[inline]
    fn default() -> RustcAlignStaticParser {
        RustcAlignStaticParser(::core::default::Default::default())
    }
}Default)]
243pub(crate) struct RustcAlignStaticParser(RustcAlignParser);
244
245impl RustcAlignStaticParser {
246    const PATH: &[Symbol] = &[sym::rustc_align_static];
247    const TEMPLATE: AttributeTemplate = RustcAlignParser::TEMPLATE;
248
249    fn parse(&mut self, cx: &mut AcceptContext<'_, '_>, args: &ArgParser) {
250        self.0.parse(cx, args)
251    }
252}
253
254impl AttributeParser for RustcAlignStaticParser {
255    const ATTRIBUTES: AcceptMapping<Self> = &[(Self::PATH, Self::TEMPLATE, Self::parse)];
256    const ALLOWED_TARGETS: AllowedTargets =
257        AllowedTargets::AllowList(&[Allow(Target::Static), Allow(Target::ForeignStatic)]);
258
259    fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {
260        let (align, span) = self.0.0?;
261        Some(AttributeKind::RustcAlign { align, span })
262    }
263}