rustc_attr_parsing/attributes/
repr.rs

1use rustc_abi::Align;
2use rustc_ast::{IntTy, LitIntType, LitKind, UintTy};
3use rustc_attr_data_structures::{AttributeKind, IntType, ReprAttr};
4use rustc_span::{DUMMY_SP, Span, Symbol, sym};
5
6use super::{CombineAttributeParser, ConvertFn};
7use crate::context::AcceptContext;
8use crate::parser::{ArgParser, MetaItemListParser, MetaItemParser};
9use crate::session_diagnostics;
10use crate::session_diagnostics::IncorrectReprFormatGenericCause;
11
12/// Parse #[repr(...)] forms.
13///
14/// Valid repr contents: any of the primitive integral type names (see
15/// `int_type_of_word`, below) to specify enum discriminant type; `C`, to use
16/// the same discriminant size that the corresponding C enum would or C
17/// structure layout, `packed` to remove padding, and `transparent` to delegate representation
18/// concerns to the only non-ZST field.
19// FIXME(jdonszelmann): is a vec the right representation here even? isn't it just a struct?
20pub(crate) struct ReprParser;
21
22impl CombineAttributeParser for ReprParser {
23    type Item = (ReprAttr, Span);
24    const PATH: &'static [Symbol] = &[sym::repr];
25    const CONVERT: ConvertFn<Self::Item> = AttributeKind::Repr;
26
27    fn extend<'a>(
28        cx: &'a AcceptContext<'a>,
29        args: &'a ArgParser<'a>,
30    ) -> impl IntoIterator<Item = Self::Item> + 'a {
31        let mut reprs = Vec::new();
32
33        let Some(list) = args.list() else {
34            return reprs;
35        };
36
37        if list.is_empty() {
38            // this is so validation can emit a lint
39            reprs.push((ReprAttr::ReprEmpty, cx.attr_span));
40        }
41
42        for param in list.mixed() {
43            if let Some(_) = param.lit() {
44                cx.emit_err(session_diagnostics::ReprIdent { span: cx.attr_span });
45                continue;
46            }
47
48            reprs.extend(
49                param.meta_item().and_then(|mi| parse_repr(cx, &mi)).map(|r| (r, param.span())),
50            );
51        }
52
53        reprs
54    }
55}
56
57macro_rules! int_pat {
58    () => {
59        sym::i8
60            | sym::u8
61            | sym::i16
62            | sym::u16
63            | sym::i32
64            | sym::u32
65            | sym::i64
66            | sym::u64
67            | sym::i128
68            | sym::u128
69            | sym::isize
70            | sym::usize
71    };
72}
73
74fn int_type_of_word(s: Symbol) -> Option<IntType> {
75    use IntType::*;
76
77    match s {
78        sym::i8 => Some(SignedInt(IntTy::I8)),
79        sym::u8 => Some(UnsignedInt(UintTy::U8)),
80        sym::i16 => Some(SignedInt(IntTy::I16)),
81        sym::u16 => Some(UnsignedInt(UintTy::U16)),
82        sym::i32 => Some(SignedInt(IntTy::I32)),
83        sym::u32 => Some(UnsignedInt(UintTy::U32)),
84        sym::i64 => Some(SignedInt(IntTy::I64)),
85        sym::u64 => Some(UnsignedInt(UintTy::U64)),
86        sym::i128 => Some(SignedInt(IntTy::I128)),
87        sym::u128 => Some(UnsignedInt(UintTy::U128)),
88        sym::isize => Some(SignedInt(IntTy::Isize)),
89        sym::usize => Some(UnsignedInt(UintTy::Usize)),
90        _ => None,
91    }
92}
93
94fn parse_repr(cx: &AcceptContext<'_>, param: &MetaItemParser<'_>) -> Option<ReprAttr> {
95    use ReprAttr::*;
96
97    // FIXME(jdonszelmann): invert the parsing here to match on the word first and then the
98    // structure.
99    let (name, ident_span) = if let Some(ident) = param.path_without_args().word() {
100        (Some(ident.name), ident.span)
101    } else {
102        (None, DUMMY_SP)
103    };
104
105    let args = param.args();
106
107    match (name, args) {
108        (Some(sym::align), ArgParser::NoArgs) => {
109            cx.emit_err(session_diagnostics::InvalidReprAlignNeedArg { span: ident_span });
110            None
111        }
112        (Some(sym::align), ArgParser::List(l)) => {
113            parse_repr_align(cx, l, param.span(), AlignKind::Align)
114        }
115
116        (Some(sym::packed), ArgParser::NoArgs) => Some(ReprPacked(Align::ONE)),
117        (Some(sym::packed), ArgParser::List(l)) => {
118            parse_repr_align(cx, l, param.span(), AlignKind::Packed)
119        }
120
121        (Some(name @ sym::align | name @ sym::packed), ArgParser::NameValue(l)) => {
122            cx.emit_err(session_diagnostics::IncorrectReprFormatGeneric {
123                span: param.span(),
124                // FIXME(jdonszelmann) can just be a string in the diag type
125                repr_arg: name,
126                cause: IncorrectReprFormatGenericCause::from_lit_kind(
127                    param.span(),
128                    &l.value_as_lit().kind,
129                    name,
130                ),
131            });
132            None
133        }
134
135        (Some(sym::Rust), ArgParser::NoArgs) => Some(ReprRust),
136        (Some(sym::C), ArgParser::NoArgs) => Some(ReprC),
137        (Some(sym::simd), ArgParser::NoArgs) => Some(ReprSimd),
138        (Some(sym::transparent), ArgParser::NoArgs) => Some(ReprTransparent),
139        (Some(name @ int_pat!()), ArgParser::NoArgs) => {
140            // int_pat!() should make sure it always parses
141            Some(ReprInt(int_type_of_word(name).unwrap()))
142        }
143
144        (
145            Some(
146                name @ sym::Rust
147                | name @ sym::C
148                | name @ sym::simd
149                | name @ sym::transparent
150                | name @ int_pat!(),
151            ),
152            ArgParser::NameValue(_),
153        ) => {
154            cx.emit_err(session_diagnostics::InvalidReprHintNoValue { span: param.span(), name });
155            None
156        }
157        (
158            Some(
159                name @ sym::Rust
160                | name @ sym::C
161                | name @ sym::simd
162                | name @ sym::transparent
163                | name @ int_pat!(),
164            ),
165            ArgParser::List(_),
166        ) => {
167            cx.emit_err(session_diagnostics::InvalidReprHintNoParen { span: param.span(), name });
168            None
169        }
170
171        _ => {
172            cx.emit_err(session_diagnostics::UnrecognizedReprHint { span: param.span() });
173            None
174        }
175    }
176}
177
178enum AlignKind {
179    Packed,
180    Align,
181}
182
183fn parse_repr_align(
184    cx: &AcceptContext<'_>,
185    list: &MetaItemListParser<'_>,
186    param_span: Span,
187    align_kind: AlignKind,
188) -> Option<ReprAttr> {
189    use AlignKind::*;
190
191    let Some(align) = list.single() else {
192        match align_kind {
193            Packed => {
194                cx.emit_err(session_diagnostics::IncorrectReprFormatPackedOneOrZeroArg {
195                    span: param_span,
196                });
197            }
198            Align => {
199                cx.dcx().emit_err(session_diagnostics::IncorrectReprFormatAlignOneArg {
200                    span: param_span,
201                });
202            }
203        }
204
205        return None;
206    };
207
208    let Some(lit) = align.lit() else {
209        match align_kind {
210            Packed => {
211                cx.emit_err(session_diagnostics::IncorrectReprFormatPackedExpectInteger {
212                    span: align.span(),
213                });
214            }
215            Align => {
216                cx.emit_err(session_diagnostics::IncorrectReprFormatExpectInteger {
217                    span: align.span(),
218                });
219            }
220        }
221
222        return None;
223    };
224
225    match parse_alignment(&lit.kind) {
226        Ok(literal) => Some(match align_kind {
227            AlignKind::Packed => ReprAttr::ReprPacked(literal),
228            AlignKind::Align => ReprAttr::ReprAlign(literal),
229        }),
230        Err(message) => {
231            cx.emit_err(session_diagnostics::InvalidReprGeneric {
232                span: lit.span,
233                repr_arg: match align_kind {
234                    Packed => "packed".to_string(),
235                    Align => "align".to_string(),
236                },
237                error_part: message,
238            });
239            None
240        }
241    }
242}
243
244fn parse_alignment(node: &LitKind) -> Result<Align, &'static str> {
245    if let LitKind::Int(literal, LitIntType::Unsuffixed) = node {
246        // `Align::from_bytes` accepts 0 as an input, check is_power_of_two() first
247        if literal.get().is_power_of_two() {
248            // Only possible error is larger than 2^29
249            literal
250                .get()
251                .try_into()
252                .ok()
253                .and_then(|v| Align::from_bytes(v).ok())
254                .ok_or("larger than 2^29")
255        } else {
256            Err("not a power of two")
257        }
258    } else {
259        Err("not an unsuffixed integer")
260    }
261}