rustc_attr_parsing/attributes/
repr.rs

1//! Parsing and validation of builtin attributes
2
3use rustc_abi::Align;
4use rustc_ast::attr::AttributeExt;
5use rustc_ast::{self as ast, MetaItemKind};
6use rustc_attr_data_structures::IntType;
7use rustc_attr_data_structures::ReprAttr::*;
8use rustc_session::Session;
9use rustc_span::{Symbol, sym};
10
11use crate::ReprAttr;
12use crate::session_diagnostics::{self, IncorrectReprFormatGenericCause};
13
14/// Parse #[repr(...)] forms.
15///
16/// Valid repr contents: any of the primitive integral type names (see
17/// `int_type_of_word`, below) to specify enum discriminant type; `C`, to use
18/// the same discriminant size that the corresponding C enum would or C
19/// structure layout, `packed` to remove padding, and `transparent` to delegate representation
20/// concerns to the only non-ZST field.
21pub fn find_repr_attrs(sess: &Session, attr: &impl AttributeExt) -> Vec<ReprAttr> {
22    if attr.has_name(sym::repr) { parse_repr_attr(sess, attr) } else { Vec::new() }
23}
24
25pub fn parse_repr_attr(sess: &Session, attr: &impl AttributeExt) -> Vec<ReprAttr> {
26    assert!(attr.has_name(sym::repr), "expected `#[repr(..)]`, found: {attr:?}");
27    let mut acc = Vec::new();
28    let dcx = sess.dcx();
29
30    if let Some(items) = attr.meta_item_list() {
31        for item in items {
32            let mut recognised = false;
33            if item.is_word() {
34                let hint = match item.name_or_empty() {
35                    sym::Rust => Some(ReprRust),
36                    sym::C => Some(ReprC),
37                    sym::packed => Some(ReprPacked(Align::ONE)),
38                    sym::simd => Some(ReprSimd),
39                    sym::transparent => Some(ReprTransparent),
40                    sym::align => {
41                        sess.dcx().emit_err(session_diagnostics::InvalidReprAlignNeedArg {
42                            span: item.span(),
43                        });
44                        recognised = true;
45                        None
46                    }
47                    name => int_type_of_word(name).map(ReprInt),
48                };
49
50                if let Some(h) = hint {
51                    recognised = true;
52                    acc.push(h);
53                }
54            } else if let Some((name, value)) = item.singleton_lit_list() {
55                let mut literal_error = None;
56                let mut err_span = item.span();
57                if name == sym::align {
58                    recognised = true;
59                    match parse_alignment(&value.kind) {
60                        Ok(literal) => acc.push(ReprAlign(literal)),
61                        Err(message) => {
62                            err_span = value.span;
63                            literal_error = Some(message)
64                        }
65                    };
66                } else if name == sym::packed {
67                    recognised = true;
68                    match parse_alignment(&value.kind) {
69                        Ok(literal) => acc.push(ReprPacked(literal)),
70                        Err(message) => {
71                            err_span = value.span;
72                            literal_error = Some(message)
73                        }
74                    };
75                } else if matches!(name, sym::Rust | sym::C | sym::simd | sym::transparent)
76                    || int_type_of_word(name).is_some()
77                {
78                    recognised = true;
79                    sess.dcx().emit_err(session_diagnostics::InvalidReprHintNoParen {
80                        span: item.span(),
81                        name: name.to_ident_string(),
82                    });
83                }
84                if let Some(literal_error) = literal_error {
85                    sess.dcx().emit_err(session_diagnostics::InvalidReprGeneric {
86                        span: err_span,
87                        repr_arg: name.to_ident_string(),
88                        error_part: literal_error,
89                    });
90                }
91            } else if let Some(meta_item) = item.meta_item() {
92                match &meta_item.kind {
93                    MetaItemKind::NameValue(value) => {
94                        if meta_item.has_name(sym::align) || meta_item.has_name(sym::packed) {
95                            let name = meta_item.name_or_empty().to_ident_string();
96                            recognised = true;
97                            sess.dcx().emit_err(session_diagnostics::IncorrectReprFormatGeneric {
98                                span: item.span(),
99                                repr_arg: &name,
100                                cause: IncorrectReprFormatGenericCause::from_lit_kind(
101                                    item.span(),
102                                    &value.kind,
103                                    &name,
104                                ),
105                            });
106                        } else if matches!(
107                            meta_item.name_or_empty(),
108                            sym::Rust | sym::C | sym::simd | sym::transparent
109                        ) || int_type_of_word(meta_item.name_or_empty()).is_some()
110                        {
111                            recognised = true;
112                            sess.dcx().emit_err(session_diagnostics::InvalidReprHintNoValue {
113                                span: meta_item.span,
114                                name: meta_item.name_or_empty().to_ident_string(),
115                            });
116                        }
117                    }
118                    MetaItemKind::List(nested_items) => {
119                        if meta_item.has_name(sym::align) {
120                            recognised = true;
121                            if let [nested_item] = nested_items.as_slice() {
122                                sess.dcx().emit_err(
123                                    session_diagnostics::IncorrectReprFormatExpectInteger {
124                                        span: nested_item.span(),
125                                    },
126                                );
127                            } else {
128                                sess.dcx().emit_err(
129                                    session_diagnostics::IncorrectReprFormatAlignOneArg {
130                                        span: meta_item.span,
131                                    },
132                                );
133                            }
134                        } else if meta_item.has_name(sym::packed) {
135                            recognised = true;
136                            if let [nested_item] = nested_items.as_slice() {
137                                sess.dcx().emit_err(
138                                    session_diagnostics::IncorrectReprFormatPackedExpectInteger {
139                                        span: nested_item.span(),
140                                    },
141                                );
142                            } else {
143                                sess.dcx().emit_err(
144                                    session_diagnostics::IncorrectReprFormatPackedOneOrZeroArg {
145                                        span: meta_item.span,
146                                    },
147                                );
148                            }
149                        } else if matches!(
150                            meta_item.name_or_empty(),
151                            sym::Rust | sym::C | sym::simd | sym::transparent
152                        ) || int_type_of_word(meta_item.name_or_empty()).is_some()
153                        {
154                            recognised = true;
155                            sess.dcx().emit_err(session_diagnostics::InvalidReprHintNoParen {
156                                span: meta_item.span,
157                                name: meta_item.name_or_empty().to_ident_string(),
158                            });
159                        }
160                    }
161                    _ => (),
162                }
163            }
164            if !recognised {
165                // Not a word we recognize. This will be caught and reported by
166                // the `check_mod_attrs` pass, but this pass doesn't always run
167                // (e.g. if we only pretty-print the source), so we have to gate
168                // the `span_delayed_bug` call as follows:
169                if sess.opts.pretty.is_none_or(|pp| pp.needs_analysis()) {
170                    dcx.span_delayed_bug(item.span(), "unrecognized representation hint");
171                }
172            }
173        }
174    }
175    acc
176}
177
178fn int_type_of_word(s: Symbol) -> Option<IntType> {
179    use rustc_attr_data_structures::IntType::*;
180
181    match s {
182        sym::i8 => Some(SignedInt(ast::IntTy::I8)),
183        sym::u8 => Some(UnsignedInt(ast::UintTy::U8)),
184        sym::i16 => Some(SignedInt(ast::IntTy::I16)),
185        sym::u16 => Some(UnsignedInt(ast::UintTy::U16)),
186        sym::i32 => Some(SignedInt(ast::IntTy::I32)),
187        sym::u32 => Some(UnsignedInt(ast::UintTy::U32)),
188        sym::i64 => Some(SignedInt(ast::IntTy::I64)),
189        sym::u64 => Some(UnsignedInt(ast::UintTy::U64)),
190        sym::i128 => Some(SignedInt(ast::IntTy::I128)),
191        sym::u128 => Some(UnsignedInt(ast::UintTy::U128)),
192        sym::isize => Some(SignedInt(ast::IntTy::Isize)),
193        sym::usize => Some(UnsignedInt(ast::UintTy::Usize)),
194        _ => None,
195    }
196}
197
198pub fn parse_alignment(node: &ast::LitKind) -> Result<Align, &'static str> {
199    if let ast::LitKind::Int(literal, ast::LitIntType::Unsuffixed) = node {
200        // `Align::from_bytes` accepts 0 as an input, check is_power_of_two() first
201        if literal.get().is_power_of_two() {
202            // Only possible error is larger than 2^29
203            literal
204                .get()
205                .try_into()
206                .ok()
207                .and_then(|v| Align::from_bytes(v).ok())
208                .ok_or("larger than 2^29")
209        } else {
210            Err("not a power of two")
211        }
212    } else {
213        Err("not an unsuffixed integer")
214    }
215}