| 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
| 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
| 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| 4 | |
| 5 | use syn::ext::IdentExt; |
| 6 | |
| 7 | use crate::bindgen::ir::ty::{IntKind, PrimitiveType}; |
| 8 | |
| 9 | #[derive (Debug, Copy, Clone, PartialEq, Eq, Default)] |
| 10 | pub enum ReprStyle { |
| 11 | #[default] |
| 12 | Rust, |
| 13 | C, |
| 14 | Transparent, |
| 15 | } |
| 16 | |
| 17 | #[derive (Debug, Copy, Clone, PartialEq, Eq)] |
| 18 | pub struct ReprType { |
| 19 | kind: IntKind, |
| 20 | signed: bool, |
| 21 | } |
| 22 | |
| 23 | impl ReprType { |
| 24 | pub(crate) fn to_primitive(self) -> PrimitiveType { |
| 25 | PrimitiveType::Integer { |
| 26 | kind: self.kind, |
| 27 | signed: self.signed, |
| 28 | zeroable: true, |
| 29 | } |
| 30 | } |
| 31 | } |
| 32 | |
| 33 | #[derive (Debug, Copy, Clone, PartialEq, Eq)] |
| 34 | pub enum ReprAlign { |
| 35 | Packed, |
| 36 | Align(u64), |
| 37 | } |
| 38 | |
| 39 | #[derive (Debug, Copy, Clone, PartialEq, Eq, Default)] |
| 40 | pub struct Repr { |
| 41 | pub style: ReprStyle, |
| 42 | pub ty: Option<ReprType>, |
| 43 | pub align: Option<ReprAlign>, |
| 44 | } |
| 45 | |
| 46 | impl Repr { |
| 47 | pub fn load(attrs: &[syn::Attribute]) -> Result<Repr, String> { |
| 48 | let ids = attrs |
| 49 | .iter() |
| 50 | .filter_map(|attr| { |
| 51 | if let syn::Meta::List(syn::MetaList { path, nested, .. }) = |
| 52 | attr.parse_meta().ok()? |
| 53 | { |
| 54 | if path.is_ident("repr" ) { |
| 55 | return Some(nested.into_iter().collect::<Vec<_>>()); |
| 56 | } |
| 57 | } |
| 58 | None |
| 59 | }) |
| 60 | .flatten() |
| 61 | .filter_map(|meta| match meta { |
| 62 | syn::NestedMeta::Meta(syn::Meta::Path(path)) => Some(( |
| 63 | path.segments.first().unwrap().ident.unraw().to_string(), |
| 64 | None, |
| 65 | )), |
| 66 | syn::NestedMeta::Meta(syn::Meta::List(syn::MetaList { path, nested, .. })) => { |
| 67 | Some(( |
| 68 | path.segments.first().unwrap().ident.unraw().to_string(), |
| 69 | Some( |
| 70 | nested |
| 71 | .iter() |
| 72 | .filter_map(|meta| match meta { |
| 73 | // Only used for #[repr(align(...))]. |
| 74 | syn::NestedMeta::Lit(syn::Lit::Int(literal)) => { |
| 75 | Some(literal.base10_digits().to_string()) |
| 76 | } |
| 77 | // Only single levels of nesting supported at the moment. |
| 78 | _ => None, |
| 79 | }) |
| 80 | .collect::<Vec<_>>(), |
| 81 | ), |
| 82 | )) |
| 83 | } |
| 84 | _ => None, |
| 85 | }); |
| 86 | |
| 87 | let mut repr = Repr::default(); |
| 88 | for id in ids { |
| 89 | let (int_kind, signed) = match (id.0.as_ref(), id.1) { |
| 90 | ("u8" , None) => (IntKind::B8, false), |
| 91 | ("u16" , None) => (IntKind::B16, false), |
| 92 | ("u32" , None) => (IntKind::B32, false), |
| 93 | ("u64" , None) => (IntKind::B64, false), |
| 94 | ("usize" , None) => (IntKind::Size, false), |
| 95 | ("i8" , None) => (IntKind::B8, true), |
| 96 | ("i16" , None) => (IntKind::B16, true), |
| 97 | ("i32" , None) => (IntKind::B32, true), |
| 98 | ("i64" , None) => (IntKind::B64, true), |
| 99 | ("isize" , None) => (IntKind::Size, true), |
| 100 | ("C" , None) => { |
| 101 | repr.style = ReprStyle::C; |
| 102 | continue; |
| 103 | } |
| 104 | ("transparent" , None) => { |
| 105 | repr.style = ReprStyle::Transparent; |
| 106 | continue; |
| 107 | } |
| 108 | ("packed" , args) => { |
| 109 | // #[repr(packed(n))] not supported because of some open questions about how |
| 110 | // to calculate the native alignment of types. See mozilla/cbindgen#433. |
| 111 | if args.is_some() { |
| 112 | return Err( |
| 113 | "Not-yet-implemented #[repr(packed(...))] encountered." .to_string() |
| 114 | ); |
| 115 | } |
| 116 | let align = ReprAlign::Packed; |
| 117 | // Only permit a single alignment-setting repr. |
| 118 | if let Some(old_align) = repr.align { |
| 119 | return Err(format!( |
| 120 | "Conflicting #[repr(align(...))] type hints {:?} and {:?}." , |
| 121 | old_align, align |
| 122 | )); |
| 123 | } |
| 124 | repr.align = Some(align); |
| 125 | continue; |
| 126 | } |
| 127 | ("align" , Some(args)) => { |
| 128 | // #[repr(align(...))] only allows a single argument. |
| 129 | if args.len() != 1 { |
| 130 | return Err(format!( |
| 131 | "Unsupported #[repr(align( {}))], align must have exactly one argument." , |
| 132 | args.join(", " ) |
| 133 | )); |
| 134 | } |
| 135 | // Must be a positive integer. |
| 136 | let align = match args.first().unwrap().parse::<u64>() { |
| 137 | Ok(align) => align, |
| 138 | Err(_) => { |
| 139 | return Err(format!("Non-numeric #[repr(align( {}))]." , args.join(", " ))) |
| 140 | } |
| 141 | }; |
| 142 | // Must be a power of 2. |
| 143 | if !align.is_power_of_two() || align == 0 { |
| 144 | return Err(format!("Invalid alignment to #[repr(align( {}))]." , align)); |
| 145 | } |
| 146 | // Only permit a single alignment-setting repr. |
| 147 | if let Some(old_align) = repr.align { |
| 148 | return Err(format!( |
| 149 | "Conflicting #[repr(align(...))] type hints {:?} and {:?}." , |
| 150 | old_align, |
| 151 | ReprAlign::Align(align) |
| 152 | )); |
| 153 | } |
| 154 | repr.align = Some(ReprAlign::Align(align)); |
| 155 | continue; |
| 156 | } |
| 157 | (path, args) => match args { |
| 158 | None => return Err(format!("Unsupported #[repr( {})]." , path)), |
| 159 | Some(args) => { |
| 160 | return Err(format!( |
| 161 | "Unsupported #[repr( {}( {}))]." , |
| 162 | path, |
| 163 | args.join(", " ) |
| 164 | )); |
| 165 | } |
| 166 | }, |
| 167 | }; |
| 168 | let ty = ReprType { |
| 169 | kind: int_kind, |
| 170 | signed, |
| 171 | }; |
| 172 | if let Some(old_ty) = repr.ty { |
| 173 | return Err(format!( |
| 174 | "Conflicting #[repr(...)] type hints {:?} and {:?}." , |
| 175 | old_ty, ty |
| 176 | )); |
| 177 | } |
| 178 | repr.ty = Some(ty); |
| 179 | } |
| 180 | Ok(repr) |
| 181 | } |
| 182 | } |
| 183 | |