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::{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 [rustc_span::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 (ident, args) = param.word_or_empty();
100
101    match (ident.name, args) {
102        (sym::align, ArgParser::NoArgs) => {
103            cx.emit_err(session_diagnostics::InvalidReprAlignNeedArg { span: ident.span });
104            None
105        }
106        (sym::align, ArgParser::List(l)) => parse_repr_align(cx, l, param.span(), AlignKind::Align),
107
108        (sym::packed, ArgParser::NoArgs) => Some(ReprPacked(Align::ONE)),
109        (sym::packed, ArgParser::List(l)) => {
110            parse_repr_align(cx, l, param.span(), AlignKind::Packed)
111        }
112
113        (sym::align | sym::packed, ArgParser::NameValue(l)) => {
114            cx.emit_err(session_diagnostics::IncorrectReprFormatGeneric {
115                span: param.span(),
116                // FIXME(jdonszelmann) can just be a string in the diag type
117                repr_arg: &ident.to_string(),
118                cause: IncorrectReprFormatGenericCause::from_lit_kind(
119                    param.span(),
120                    &l.value_as_lit().kind,
121                    ident.name.as_str(),
122                ),
123            });
124            None
125        }
126
127        (sym::Rust, ArgParser::NoArgs) => Some(ReprRust),
128        (sym::C, ArgParser::NoArgs) => Some(ReprC),
129        (sym::simd, ArgParser::NoArgs) => Some(ReprSimd),
130        (sym::transparent, ArgParser::NoArgs) => Some(ReprTransparent),
131        (i @ int_pat!(), ArgParser::NoArgs) => {
132            // int_pat!() should make sure it always parses
133            Some(ReprInt(int_type_of_word(i).unwrap()))
134        }
135
136        (
137            sym::Rust | sym::C | sym::simd | sym::transparent | int_pat!(),
138            ArgParser::NameValue(_),
139        ) => {
140            cx.emit_err(session_diagnostics::InvalidReprHintNoValue {
141                span: param.span(),
142                name: ident.to_string(),
143            });
144            None
145        }
146        (sym::Rust | sym::C | sym::simd | sym::transparent | int_pat!(), ArgParser::List(_)) => {
147            cx.emit_err(session_diagnostics::InvalidReprHintNoParen {
148                span: param.span(),
149                name: ident.to_string(),
150            });
151            None
152        }
153
154        _ => {
155            cx.emit_err(session_diagnostics::UnrecognizedReprHint { span: param.span() });
156            None
157        }
158    }
159}
160
161enum AlignKind {
162    Packed,
163    Align,
164}
165
166fn parse_repr_align(
167    cx: &AcceptContext<'_>,
168    list: &MetaItemListParser<'_>,
169    param_span: Span,
170    align_kind: AlignKind,
171) -> Option<ReprAttr> {
172    use AlignKind::*;
173
174    let Some(align) = list.single() else {
175        match align_kind {
176            Packed => {
177                cx.emit_err(session_diagnostics::IncorrectReprFormatPackedOneOrZeroArg {
178                    span: param_span,
179                });
180            }
181            Align => {
182                cx.dcx().emit_err(session_diagnostics::IncorrectReprFormatAlignOneArg {
183                    span: param_span,
184                });
185            }
186        }
187
188        return None;
189    };
190
191    let Some(lit) = align.lit() else {
192        match align_kind {
193            Packed => {
194                cx.emit_err(session_diagnostics::IncorrectReprFormatPackedExpectInteger {
195                    span: align.span(),
196                });
197            }
198            Align => {
199                cx.emit_err(session_diagnostics::IncorrectReprFormatExpectInteger {
200                    span: align.span(),
201                });
202            }
203        }
204
205        return None;
206    };
207
208    match parse_alignment(&lit.kind) {
209        Ok(literal) => Some(match align_kind {
210            AlignKind::Packed => ReprAttr::ReprPacked(literal),
211            AlignKind::Align => ReprAttr::ReprAlign(literal),
212        }),
213        Err(message) => {
214            cx.emit_err(session_diagnostics::InvalidReprGeneric {
215                span: lit.span,
216                repr_arg: match align_kind {
217                    Packed => "packed".to_string(),
218                    Align => "align".to_string(),
219                },
220                error_part: message,
221            });
222            None
223        }
224    }
225}
226
227fn parse_alignment(node: &LitKind) -> Result<Align, &'static str> {
228    if let LitKind::Int(literal, LitIntType::Unsuffixed) = node {
229        // `Align::from_bytes` accepts 0 as an input, check is_power_of_two() first
230        if literal.get().is_power_of_two() {
231            // Only possible error is larger than 2^29
232            literal
233                .get()
234                .try_into()
235                .ok()
236                .and_then(|v| Align::from_bytes(v).ok())
237                .ok_or("larger than 2^29")
238        } else {
239            Err("not a power of two")
240        }
241    } else {
242        Err("not an unsuffixed integer")
243    }
244}