1 // SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT 2 // 3 // Copyright 2019 The Fuchsia Authors 4 // 5 // Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 6 // <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT 7 // license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. 8 // This file may not be copied, modified, or distributed except according to 9 // those terms. 10 11 use core::{ 12 convert::{Infallible, TryFrom}, 13 num::NonZeroU32, 14 }; 15 16 use proc_macro2::{Span, TokenStream}; 17 use quote::{quote_spanned, ToTokens, TokenStreamExt as _}; 18 use syn::{ 19 punctuated::Punctuated, spanned::Spanned as _, token::Comma, Attribute, Error, LitInt, Meta, 20 MetaList, 21 }; 22 23 use crate::util::{path_is_ident, to_ident_str}; 24 25 /// The computed representation of a type. 26 /// 27 /// This is the result of processing all `#[repr(...)]` attributes on a type, if 28 /// any. A `Repr` is only capable of representing legal combinations of 29 /// `#[repr(...)]` attributes. 30 #[cfg_attr(test, derive(Copy, Clone, Debug))] 31 pub(crate) enum Repr<Prim, Packed> { 32 /// `#[repr(transparent)]` 33 Transparent(Span), 34 /// A compound representation: `repr(C)`, `repr(Rust)`, or `repr(Int)` 35 /// optionally combined with `repr(packed(...))` or `repr(align(...))` 36 Compound(Spanned<CompoundRepr<Prim>>, Option<Spanned<AlignRepr<Packed>>>), 37 } 38 39 /// A compound representation: `repr(C)`, `repr(Rust)`, or `repr(Int)`. 40 #[cfg_attr(test, derive(Copy, Clone, Debug, Eq, PartialEq))] 41 pub(crate) enum CompoundRepr<Prim> { 42 C, 43 Rust, 44 Primitive(Prim), 45 } 46 47 /// `repr(Int)` 48 #[derive(Copy, Clone)] 49 #[cfg_attr(test, derive(Debug, Eq, PartialEq))] 50 pub(crate) enum PrimitiveRepr { 51 U8, 52 U16, 53 U32, 54 U64, 55 U128, 56 Usize, 57 I8, 58 I16, 59 I32, 60 I64, 61 I128, 62 Isize, 63 } 64 65 /// `repr(packed(...))` or `repr(align(...))` 66 #[cfg_attr(test, derive(Copy, Clone, Debug, Eq, PartialEq))] 67 pub(crate) enum AlignRepr<Packed> { 68 Packed(Packed), 69 Align(NonZeroU32), 70 } 71 72 /// The representations which can legally appear on a struct or union type. 73 pub(crate) type StructUnionRepr = Repr<Infallible, NonZeroU32>; 74 75 /// The representations which can legally appear on an enum type. 76 pub(crate) type EnumRepr = Repr<PrimitiveRepr, Infallible>; 77 78 impl<Prim, Packed> Repr<Prim, Packed> { 79 /// Gets the name of this "repr type" - the non-align `repr(X)` that is used 80 /// in prose to refer to this type. 81 /// 82 /// For example, we would refer to `#[repr(C, align(4))] struct Foo { ... }` 83 /// as a "`repr(C)` struct". 84 pub(crate) fn repr_type_name(&self) -> &str 85 where 86 Prim: Copy + With<PrimitiveRepr>, 87 { 88 use CompoundRepr::*; 89 use PrimitiveRepr::*; 90 use Repr::*; 91 match self { 92 Transparent(_span) => "repr(transparent)", 93 Compound(Spanned { t: repr, span: _ }, _align) => match repr { 94 C => "repr(C)", 95 Rust => "repr(Rust)", 96 Primitive(prim) => prim.with(|prim| match prim { 97 U8 => "repr(u8)", 98 U16 => "repr(u16)", 99 U32 => "repr(u32)", 100 U64 => "repr(u64)", 101 U128 => "repr(u128)", 102 Usize => "repr(usize)", 103 I8 => "repr(i8)", 104 I16 => "repr(i16)", 105 I32 => "repr(i32)", 106 I64 => "repr(i64)", 107 I128 => "repr(i128)", 108 Isize => "repr(isize)", 109 }), 110 }, 111 } 112 } 113 114 pub(crate) fn is_transparent(&self) -> bool { 115 matches!(self, Repr::Transparent(_)) 116 } 117 118 pub(crate) fn is_c(&self) -> bool { 119 use CompoundRepr::*; 120 matches!(self, Repr::Compound(Spanned { t: C, span: _ }, _align)) 121 } 122 123 pub(crate) fn is_primitive(&self) -> bool { 124 use CompoundRepr::*; 125 matches!(self, Repr::Compound(Spanned { t: Primitive(_), span: _ }, _align)) 126 } 127 128 pub(crate) fn get_packed(&self) -> Option<&Packed> { 129 use AlignRepr::*; 130 use Repr::*; 131 if let Compound(_, Some(Spanned { t: Packed(p), span: _ })) = self { 132 Some(p) 133 } else { 134 None 135 } 136 } 137 138 pub(crate) fn get_align(&self) -> Option<Spanned<NonZeroU32>> { 139 use AlignRepr::*; 140 use Repr::*; 141 if let Compound(_, Some(Spanned { t: Align(n), span })) = self { 142 Some(Spanned::new(*n, *span)) 143 } else { 144 None 145 } 146 } 147 148 pub(crate) fn is_align_gt_1(&self) -> bool { 149 self.get_align().map(|n| n.t.get() > 1).unwrap_or(false) 150 } 151 152 /// When deriving `Unaligned`, validate that the decorated type has no 153 /// `#[repr(align(N))]` attribute where `N > 1`. If no such attribute exists 154 /// (including if `N == 1`), this returns `Ok(())`, and otherwise it returns 155 /// a descriptive error. 156 pub(crate) fn unaligned_validate_no_align_gt_1(&self) -> Result<(), Error> { 157 if let Some(n) = self.get_align().filter(|n| n.t.get() > 1) { 158 Err(Error::new( 159 n.span, 160 "cannot derive `Unaligned` on type with alignment greater than 1", 161 )) 162 } else { 163 Ok(()) 164 } 165 } 166 } 167 168 impl<Prim> Repr<Prim, NonZeroU32> { 169 /// Does `self` describe a `#[repr(packed)]` or `#[repr(packed(1))]` type? 170 pub(crate) fn is_packed_1(&self) -> bool { 171 self.get_packed().map(|n| n.get() == 1).unwrap_or(false) 172 } 173 } 174 175 impl<Packed> Repr<PrimitiveRepr, Packed> { 176 fn get_primitive(&self) -> Option<&PrimitiveRepr> { 177 use CompoundRepr::*; 178 use Repr::*; 179 if let Compound(Spanned { t: Primitive(p), span: _ }, _align) = self { 180 Some(p) 181 } else { 182 None 183 } 184 } 185 186 /// Does `self` describe a `#[repr(u8)]` type? 187 pub(crate) fn is_u8(&self) -> bool { 188 matches!(self.get_primitive(), Some(PrimitiveRepr::U8)) 189 } 190 191 /// Does `self` describe a `#[repr(i8)]` type? 192 pub(crate) fn is_i8(&self) -> bool { 193 matches!(self.get_primitive(), Some(PrimitiveRepr::I8)) 194 } 195 } 196 197 impl<Prim, Packed> ToTokens for Repr<Prim, Packed> 198 where 199 Prim: With<PrimitiveRepr> + Copy, 200 Packed: With<NonZeroU32> + Copy, 201 { 202 fn to_tokens(&self, ts: &mut TokenStream) { 203 use Repr::*; 204 match self { 205 Transparent(span) => ts.append_all(quote_spanned! { *span=> #[repr(transparent)] }), 206 Compound(repr, align) => { 207 repr.to_tokens(ts); 208 if let Some(align) = align { 209 align.to_tokens(ts); 210 } 211 } 212 } 213 } 214 } 215 216 impl<Prim: With<PrimitiveRepr> + Copy> ToTokens for Spanned<CompoundRepr<Prim>> { 217 fn to_tokens(&self, ts: &mut TokenStream) { 218 use CompoundRepr::*; 219 match &self.t { 220 C => ts.append_all(quote_spanned! { self.span=> #[repr(C)] }), 221 Rust => ts.append_all(quote_spanned! { self.span=> #[repr(Rust)] }), 222 Primitive(prim) => prim.with(|prim| Spanned::new(prim, self.span).to_tokens(ts)), 223 } 224 } 225 } 226 227 impl ToTokens for Spanned<PrimitiveRepr> { 228 fn to_tokens(&self, ts: &mut TokenStream) { 229 use PrimitiveRepr::*; 230 match self.t { 231 U8 => ts.append_all(quote_spanned! { self.span => #[repr(u8)] }), 232 U16 => ts.append_all(quote_spanned! { self.span => #[repr(u16)] }), 233 U32 => ts.append_all(quote_spanned! { self.span => #[repr(u32)] }), 234 U64 => ts.append_all(quote_spanned! { self.span => #[repr(u64)] }), 235 U128 => ts.append_all(quote_spanned! { self.span => #[repr(u128)] }), 236 Usize => ts.append_all(quote_spanned! { self.span => #[repr(usize)] }), 237 I8 => ts.append_all(quote_spanned! { self.span => #[repr(i8)] }), 238 I16 => ts.append_all(quote_spanned! { self.span => #[repr(i16)] }), 239 I32 => ts.append_all(quote_spanned! { self.span => #[repr(i32)] }), 240 I64 => ts.append_all(quote_spanned! { self.span => #[repr(i64)] }), 241 I128 => ts.append_all(quote_spanned! { self.span => #[repr(i128)] }), 242 Isize => ts.append_all(quote_spanned! { self.span => #[repr(isize)] }), 243 } 244 } 245 } 246 247 impl<Packed: With<NonZeroU32> + Copy> ToTokens for Spanned<AlignRepr<Packed>> { 248 fn to_tokens(&self, ts: &mut TokenStream) { 249 use AlignRepr::*; 250 // We use `syn::Index` instead of `u32` because `quote_spanned!` 251 // serializes `u32` literals as `123u32`, not just `123`. Rust doesn't 252 // recognize that as a valid argument to `#[repr(align(...))]` or 253 // `#[repr(packed(...))]`. 254 let to_index = |n: NonZeroU32| syn::Index { index: n.get(), span: self.span }; 255 match self.t { 256 Packed(n) => n.with(|n| { 257 let n = to_index(n); 258 ts.append_all(quote_spanned! { self.span => #[repr(packed(#n))] }) 259 }), 260 Align(n) => { 261 let n = to_index(n); 262 ts.append_all(quote_spanned! { self.span => #[repr(align(#n))] }) 263 } 264 } 265 } 266 } 267 268 /// The result of parsing a single `#[repr(...)]` attribute or a single 269 /// directive inside a compound `#[repr(..., ...)]` attribute. 270 #[derive(Copy, Clone, PartialEq, Eq)] 271 #[cfg_attr(test, derive(Debug))] 272 pub(crate) enum RawRepr { 273 Transparent, 274 C, 275 Rust, 276 U8, 277 U16, 278 U32, 279 U64, 280 U128, 281 Usize, 282 I8, 283 I16, 284 I32, 285 I64, 286 I128, 287 Isize, 288 Align(NonZeroU32), 289 PackedN(NonZeroU32), 290 Packed, 291 } 292 293 /// The error from converting from a `RawRepr`. 294 #[cfg_attr(test, derive(Debug, Eq, PartialEq))] 295 pub(crate) enum FromRawReprError<E> { 296 /// The `RawRepr` doesn't affect the high-level repr we're parsing (e.g. 297 /// it's `align(...)` and we're parsing a `CompoundRepr`). 298 None, 299 /// The `RawRepr` is invalid for the high-level repr we're parsing (e.g. 300 /// it's `packed` repr and we're parsing an `AlignRepr` for an enum type). 301 Err(E), 302 } 303 304 /// The representation hint is not supported for the decorated type. 305 #[cfg_attr(test, derive(Copy, Clone, Debug, Eq, PartialEq))] 306 pub(crate) struct UnsupportedReprError; 307 308 impl<Prim: With<PrimitiveRepr>> TryFrom<RawRepr> for CompoundRepr<Prim> { 309 type Error = FromRawReprError<UnsupportedReprError>; 310 fn try_from( 311 raw: RawRepr, 312 ) -> Result<CompoundRepr<Prim>, FromRawReprError<UnsupportedReprError>> { 313 use RawRepr::*; 314 match raw { 315 C => Ok(CompoundRepr::C), 316 Rust => Ok(CompoundRepr::Rust), 317 raw @ (U8 | U16 | U32 | U64 | U128 | Usize | I8 | I16 | I32 | I64 | I128 | Isize) => { 318 Prim::try_with_or( 319 || match raw { 320 U8 => Ok(PrimitiveRepr::U8), 321 U16 => Ok(PrimitiveRepr::U16), 322 U32 => Ok(PrimitiveRepr::U32), 323 U64 => Ok(PrimitiveRepr::U64), 324 U128 => Ok(PrimitiveRepr::U128), 325 Usize => Ok(PrimitiveRepr::Usize), 326 I8 => Ok(PrimitiveRepr::I8), 327 I16 => Ok(PrimitiveRepr::I16), 328 I32 => Ok(PrimitiveRepr::I32), 329 I64 => Ok(PrimitiveRepr::I64), 330 I128 => Ok(PrimitiveRepr::I128), 331 Isize => Ok(PrimitiveRepr::Isize), 332 Transparent | C | Rust | Align(_) | PackedN(_) | Packed => { 333 Err(UnsupportedReprError) 334 } 335 }, 336 UnsupportedReprError, 337 ) 338 .map(CompoundRepr::Primitive) 339 .map_err(FromRawReprError::Err) 340 } 341 Transparent | Align(_) | PackedN(_) | Packed => Err(FromRawReprError::None), 342 } 343 } 344 } 345 346 impl<Pcked: With<NonZeroU32>> TryFrom<RawRepr> for AlignRepr<Pcked> { 347 type Error = FromRawReprError<UnsupportedReprError>; 348 fn try_from(raw: RawRepr) -> Result<AlignRepr<Pcked>, FromRawReprError<UnsupportedReprError>> { 349 use RawRepr::*; 350 match raw { 351 Packed | PackedN(_) => Pcked::try_with_or( 352 || match raw { 353 Packed => Ok(NonZeroU32::new(1).unwrap()), 354 PackedN(n) => Ok(n), 355 U8 | U16 | U32 | U64 | U128 | Usize | I8 | I16 | I32 | I64 | I128 | Isize 356 | Transparent | C | Rust | Align(_) => Err(UnsupportedReprError), 357 }, 358 UnsupportedReprError, 359 ) 360 .map(AlignRepr::Packed) 361 .map_err(FromRawReprError::Err), 362 Align(n) => Ok(AlignRepr::Align(n)), 363 U8 | U16 | U32 | U64 | U128 | Usize | I8 | I16 | I32 | I64 | I128 | Isize 364 | Transparent | C | Rust => Err(FromRawReprError::None), 365 } 366 } 367 } 368 369 /// The error from extracting a high-level repr type from a list of `RawRepr`s. 370 #[cfg_attr(test, derive(Copy, Clone, Debug, Eq, PartialEq))] 371 enum FromRawReprsError<E> { 372 /// One of the `RawRepr`s is invalid for the high-level repr we're parsing 373 /// (e.g. there's a `packed` repr and we're parsing an `AlignRepr` for an 374 /// enum type). 375 Single(E), 376 /// Two `RawRepr`s appear which both affect the high-level repr we're 377 /// parsing (e.g., the list is `#[repr(align(2), packed)]`). Note that we 378 /// conservatively treat redundant reprs as conflicting (e.g. 379 /// `#[repr(packed, packed)]`). 380 Conflict, 381 } 382 383 /// Tries to extract a high-level repr from a list of `RawRepr`s. 384 fn try_from_raw_reprs<'a, E, R: TryFrom<RawRepr, Error = FromRawReprError<E>>>( 385 r: impl IntoIterator<Item = &'a Spanned<RawRepr>>, 386 ) -> Result<Option<Spanned<R>>, Spanned<FromRawReprsError<E>>> { 387 // Walk the list of `RawRepr`s and attempt to convert each to an `R`. Bail 388 // if we find any errors. If we find more than one which converts to an `R`, 389 // bail with a `Conflict` error. 390 r.into_iter().try_fold(None, |found: Option<Spanned<R>>, raw| { 391 let new = match Spanned::<R>::try_from(*raw) { 392 Ok(r) => r, 393 // This `RawRepr` doesn't convert to an `R`, so keep the current 394 // found `R`, if any. 395 Err(FromRawReprError::None) => return Ok(found), 396 // This repr is unsupported for the decorated type (e.g. 397 // `repr(packed)` on an enum). 398 Err(FromRawReprError::Err(Spanned { t: err, span })) => { 399 return Err(Spanned::new(FromRawReprsError::Single(err), span)) 400 } 401 }; 402 403 if let Some(found) = found { 404 // We already found an `R`, but this `RawRepr` also converts to an 405 // `R`, so that's a conflict. 406 // 407 // `Span::join` returns `None` if the two spans are from different 408 // files or if we're not on the nightly compiler. In that case, just 409 // use `new`'s span. 410 let span = found.span.join(new.span).unwrap_or(new.span); 411 Err(Spanned::new(FromRawReprsError::Conflict, span)) 412 } else { 413 Ok(Some(new)) 414 } 415 }) 416 } 417 418 /// The error returned from [`Repr::from_attrs`]. 419 #[cfg_attr(test, derive(Copy, Clone, Debug, Eq, PartialEq))] 420 enum FromAttrsError { 421 FromRawReprs(FromRawReprsError<UnsupportedReprError>), 422 Unrecognized, 423 } 424 425 impl From<FromRawReprsError<UnsupportedReprError>> for FromAttrsError { 426 fn from(err: FromRawReprsError<UnsupportedReprError>) -> FromAttrsError { 427 FromAttrsError::FromRawReprs(err) 428 } 429 } 430 431 impl From<UnrecognizedReprError> for FromAttrsError { 432 fn from(_err: UnrecognizedReprError) -> FromAttrsError { 433 FromAttrsError::Unrecognized 434 } 435 } 436 437 impl From<Spanned<FromAttrsError>> for Error { 438 fn from(err: Spanned<FromAttrsError>) -> Error { 439 let Spanned { t: err, span } = err; 440 match err { 441 FromAttrsError::FromRawReprs(FromRawReprsError::Single( 442 _err @ UnsupportedReprError, 443 )) => Error::new(span, "unsupported representation hint for the decorated type"), 444 FromAttrsError::FromRawReprs(FromRawReprsError::Conflict) => { 445 // NOTE: This says "another" rather than "a preceding" because 446 // when one of the reprs involved is `transparent`, we detect 447 // that condition in `Repr::from_attrs`, and at that point we 448 // can't tell which repr came first, so we might report this on 449 // the first involved repr rather than the second, third, etc. 450 Error::new(span, "this conflicts with another representation hint") 451 } 452 FromAttrsError::Unrecognized => Error::new(span, "unrecognized representation hint"), 453 } 454 } 455 } 456 457 impl<Prim, Packed> Repr<Prim, Packed> { 458 fn from_attrs_inner(attrs: &[Attribute]) -> Result<Repr<Prim, Packed>, Spanned<FromAttrsError>> 459 where 460 Prim: With<PrimitiveRepr>, 461 Packed: With<NonZeroU32>, 462 { 463 let raw_reprs = RawRepr::from_attrs(attrs).map_err(Spanned::from)?; 464 465 let transparent = { 466 let mut transparents = raw_reprs.iter().filter_map(|Spanned { t, span }| match t { 467 RawRepr::Transparent => Some(span), 468 _ => None, 469 }); 470 let first = transparents.next(); 471 let second = transparents.next(); 472 match (first, second) { 473 (None, None) => None, 474 (Some(span), None) => Some(*span), 475 (Some(_), Some(second)) => { 476 return Err(Spanned::new( 477 FromAttrsError::FromRawReprs(FromRawReprsError::Conflict), 478 *second, 479 )) 480 } 481 // An iterator can't produce a value only on the second call to 482 // `.next()`. 483 (None, Some(_)) => unreachable!(), 484 } 485 }; 486 487 let compound: Option<Spanned<CompoundRepr<Prim>>> = 488 try_from_raw_reprs(raw_reprs.iter()).map_err(Spanned::from)?; 489 let align: Option<Spanned<AlignRepr<Packed>>> = 490 try_from_raw_reprs(raw_reprs.iter()).map_err(Spanned::from)?; 491 492 if let Some(span) = transparent { 493 if compound.is_some() || align.is_some() { 494 // Arbitrarily report the problem on the `transparent` span. Any 495 // span will do. 496 return Err(Spanned::new(FromRawReprsError::Conflict.into(), span)); 497 } 498 499 Ok(Repr::Transparent(span)) 500 } else { 501 Ok(Repr::Compound( 502 compound.unwrap_or(Spanned::new(CompoundRepr::Rust, Span::call_site())), 503 align, 504 )) 505 } 506 } 507 } 508 509 impl<Prim, Packed> Repr<Prim, Packed> { 510 pub(crate) fn from_attrs(attrs: &[Attribute]) -> Result<Repr<Prim, Packed>, Error> 511 where 512 Prim: With<PrimitiveRepr>, 513 Packed: With<NonZeroU32>, 514 { 515 Repr::from_attrs_inner(attrs).map_err(Into::into) 516 } 517 } 518 519 /// The representation hint could not be parsed or was unrecognized. 520 struct UnrecognizedReprError; 521 522 impl RawRepr { 523 fn from_attrs( 524 attrs: &[Attribute], 525 ) -> Result<Vec<Spanned<RawRepr>>, Spanned<UnrecognizedReprError>> { 526 let mut reprs = Vec::new(); 527 for attr in attrs { 528 // Ignore documentation attributes. 529 if path_is_ident(attr.path(), "doc") { 530 continue; 531 } 532 if let Meta::List(ref meta_list) = attr.meta { 533 if path_is_ident(&meta_list.path, "repr") { 534 let parsed: Punctuated<Meta, Comma> = 535 match meta_list.parse_args_with(Punctuated::parse_terminated) { 536 Ok(parsed) => parsed, 537 Err(_) => { 538 return Err(Spanned::new( 539 UnrecognizedReprError, 540 meta_list.tokens.span(), 541 )) 542 } 543 }; 544 for meta in parsed { 545 let s = meta.span(); 546 reprs.push( 547 RawRepr::from_meta(&meta) 548 .map(|r| Spanned::new(r, s)) 549 .map_err(|e| Spanned::new(e, s))?, 550 ); 551 } 552 } 553 } 554 } 555 556 Ok(reprs) 557 } 558 559 fn from_meta(meta: &Meta) -> Result<RawRepr, UnrecognizedReprError> { 560 let (path, list) = match meta { 561 Meta::Path(path) => (path, None), 562 Meta::List(list) => (&list.path, Some(list)), 563 _ => return Err(UnrecognizedReprError), 564 }; 565 566 let ident = path.get_ident().ok_or(UnrecognizedReprError)?; 567 568 // Only returns `Ok` for non-zero power-of-two values. 569 // `NonZeroU32::is_power_of_two` is unsupported on our MSRV. 570 #[allow(clippy::needless_nonzero_get)] 571 let parse_nzu64 = |list: &MetaList| { 572 list.parse_args::<LitInt>() 573 .and_then(|int| int.base10_parse::<NonZeroU32>()) 574 .map_err(|_| UnrecognizedReprError) 575 .and_then(|nz| { 576 if nz.get().is_power_of_two() { 577 Ok(nz) 578 } else { 579 Err(UnrecognizedReprError) 580 } 581 }) 582 }; 583 584 use RawRepr::*; 585 let ident = to_ident_str(ident); 586 Ok(match (ident.as_str(), list) { 587 ("u8", None) => U8, 588 ("u16", None) => U16, 589 ("u32", None) => U32, 590 ("u64", None) => U64, 591 ("u128", None) => U128, 592 ("usize", None) => Usize, 593 ("i8", None) => I8, 594 ("i16", None) => I16, 595 ("i32", None) => I32, 596 ("i64", None) => I64, 597 ("i128", None) => I128, 598 ("isize", None) => Isize, 599 ("C", None) => C, 600 ("transparent", None) => Transparent, 601 ("Rust", None) => Rust, 602 ("packed", None) => Packed, 603 ("packed", Some(list)) => PackedN(parse_nzu64(list)?), 604 ("align", Some(list)) => Align(parse_nzu64(list)?), 605 _ => return Err(UnrecognizedReprError), 606 }) 607 } 608 } 609 610 pub(crate) use util::*; 611 mod util { 612 use super::*; 613 /// A value with an associated span. 614 #[derive(Copy, Clone)] 615 #[cfg_attr(test, derive(Debug))] 616 pub(crate) struct Spanned<T> { 617 pub(crate) t: T, 618 pub(crate) span: Span, 619 } 620 621 impl<T> Spanned<T> { 622 pub(super) fn new(t: T, span: Span) -> Spanned<T> { 623 Spanned { t, span } 624 } 625 626 pub(super) fn from<U>(s: Spanned<U>) -> Spanned<T> 627 where 628 T: From<U>, 629 { 630 let Spanned { t: u, span } = s; 631 Spanned::new(u.into(), span) 632 } 633 634 /// Delegates to `T: TryFrom`, preserving span information in both the 635 /// success and error cases. 636 pub(super) fn try_from<E, U>( 637 u: Spanned<U>, 638 ) -> Result<Spanned<T>, FromRawReprError<Spanned<E>>> 639 where 640 T: TryFrom<U, Error = FromRawReprError<E>>, 641 { 642 let Spanned { t: u, span } = u; 643 T::try_from(u).map(|t| Spanned { t, span }).map_err(|err| match err { 644 FromRawReprError::None => FromRawReprError::None, 645 FromRawReprError::Err(e) => FromRawReprError::Err(Spanned::new(e, span)), 646 }) 647 } 648 } 649 650 // Used to permit implementing `With<T> for T: Inhabited` and for 651 // `Infallible` without a blanket impl conflict. 652 pub(crate) trait Inhabited {} 653 impl Inhabited for PrimitiveRepr {} 654 impl Inhabited for NonZeroU32 {} 655 656 pub(crate) trait With<T> { 657 fn with<O, F: FnOnce(T) -> O>(self, f: F) -> O; 658 fn try_with_or<E, F: FnOnce() -> Result<T, E>>(f: F, err: E) -> Result<Self, E> 659 where 660 Self: Sized; 661 } 662 663 impl<T: Inhabited> With<T> for T { 664 fn with<O, F: FnOnce(T) -> O>(self, f: F) -> O { 665 f(self) 666 } 667 668 fn try_with_or<E, F: FnOnce() -> Result<T, E>>(f: F, _err: E) -> Result<Self, E> { 669 f() 670 } 671 } 672 673 impl<T> With<T> for Infallible { 674 fn with<O, F: FnOnce(T) -> O>(self, _f: F) -> O { 675 match self {} 676 } 677 678 fn try_with_or<E, F: FnOnce() -> Result<T, E>>(_f: F, err: E) -> Result<Self, E> { 679 Err(err) 680 } 681 } 682 } 683 684 #[cfg(test)] 685 mod tests { 686 use syn::parse_quote; 687 688 use super::*; 689 690 impl<T> From<T> for Spanned<T> { 691 fn from(t: T) -> Spanned<T> { 692 Spanned::new(t, Span::call_site()) 693 } 694 } 695 696 // We ignore spans for equality in testing since real spans are hard to 697 // synthesize and don't implement `PartialEq`. 698 impl<T: PartialEq> PartialEq for Spanned<T> { 699 fn eq(&self, other: &Spanned<T>) -> bool { 700 self.t.eq(&other.t) 701 } 702 } 703 704 impl<T: Eq> Eq for Spanned<T> {} 705 706 impl<Prim: PartialEq, Packed: PartialEq> PartialEq for Repr<Prim, Packed> { 707 fn eq(&self, other: &Repr<Prim, Packed>) -> bool { 708 match (self, other) { 709 (Repr::Transparent(_), Repr::Transparent(_)) => true, 710 (Repr::Compound(sc, sa), Repr::Compound(oc, oa)) => (sc, sa) == (oc, oa), 711 _ => false, 712 } 713 } 714 } 715 716 fn s() -> Span { 717 Span::call_site() 718 } 719 720 #[test] 721 fn test() { 722 // Test that a given `#[repr(...)]` attribute parses and returns the 723 // given `Repr` or error. 724 macro_rules! test { 725 ($(#[$attr:meta])* => $repr:expr) => { 726 test!(@inner $(#[$attr])* => Repr => Ok($repr)); 727 }; 728 // In the error case, the caller must explicitly provide the name of 729 // the `Repr` type to assist in type inference. 730 (@error $(#[$attr:meta])* => $typ:ident => $repr:expr) => { 731 test!(@inner $(#[$attr])* => $typ => Err($repr)); 732 }; 733 (@inner $(#[$attr:meta])* => $typ:ident => $repr:expr) => { 734 let attr: Attribute = parse_quote!($(#[$attr])*); 735 let mut got = $typ::from_attrs_inner(&[attr]); 736 let expect: Result<Repr<_, _>, _> = $repr; 737 if false { 738 // Force Rust to infer `got` as having the same type as 739 // `expect`. 740 got = expect; 741 } 742 assert_eq!(got, expect, stringify!($(#[$attr])*)); 743 }; 744 } 745 746 use AlignRepr::*; 747 use CompoundRepr::*; 748 use PrimitiveRepr::*; 749 let nz = |n: u32| NonZeroU32::new(n).unwrap(); 750 751 // Rust treats raw identifiers and their ordinary spellings as the 752 // same representation hints. Exercise every spelling recognized by 753 // `RawRepr::from_meta` so that adding a hint cannot accidentally 754 // reintroduce raw-identifier-sensitive parsing. 755 macro_rules! test_raw_repr { 756 ($ordinary:meta, $raw:meta => $expected:expr) => {{ 757 let ordinary: Meta = parse_quote!($ordinary); 758 let raw: Meta = parse_quote!($raw); 759 assert_eq!(RawRepr::from_meta(&ordinary).ok().unwrap(), $expected); 760 assert_eq!(RawRepr::from_meta(&raw).ok().unwrap(), $expected); 761 }}; 762 } 763 764 test_raw_repr!(transparent, r#transparent => RawRepr::Transparent); 765 test_raw_repr!(C, r#C => RawRepr::C); 766 test_raw_repr!(Rust, r#Rust => RawRepr::Rust); 767 test_raw_repr!(u8, r#u8 => RawRepr::U8); 768 test_raw_repr!(u16, r#u16 => RawRepr::U16); 769 test_raw_repr!(u32, r#u32 => RawRepr::U32); 770 test_raw_repr!(u64, r#u64 => RawRepr::U64); 771 test_raw_repr!(u128, r#u128 => RawRepr::U128); 772 test_raw_repr!(usize, r#usize => RawRepr::Usize); 773 test_raw_repr!(i8, r#i8 => RawRepr::I8); 774 test_raw_repr!(i16, r#i16 => RawRepr::I16); 775 test_raw_repr!(i32, r#i32 => RawRepr::I32); 776 test_raw_repr!(i64, r#i64 => RawRepr::I64); 777 test_raw_repr!(i128, r#i128 => RawRepr::I128); 778 test_raw_repr!(isize, r#isize => RawRepr::Isize); 779 test_raw_repr!(align(2), r#align(2) => RawRepr::Align(nz(2))); 780 test_raw_repr!(packed, r#packed => RawRepr::Packed); 781 test_raw_repr!(packed(2), r#packed(2) => RawRepr::PackedN(nz(2))); 782 783 test!(#[repr(transparent)] => StructUnionRepr::Transparent(s())); 784 test!(#[r#repr(r#transparent)] => StructUnionRepr::Transparent(s())); 785 test!(#[repr()] => StructUnionRepr::Compound(Rust.into(), None)); 786 test!(#[repr(packed)] => StructUnionRepr::Compound(Rust.into(), Some(Packed(nz(1)).into()))); 787 test!(#[repr(packed(2))] => StructUnionRepr::Compound(Rust.into(), Some(Packed(nz(2)).into()))); 788 test!(#[repr(align(1))] => StructUnionRepr::Compound(Rust.into(), Some(Align(nz(1)).into()))); 789 test!(#[repr(align(2))] => StructUnionRepr::Compound(Rust.into(), Some(Align(nz(2)).into()))); 790 test!(#[repr(C)] => StructUnionRepr::Compound(C.into(), None)); 791 test!(#[repr(C, packed)] => StructUnionRepr::Compound(C.into(), Some(Packed(nz(1)).into()))); 792 test!(#[repr(C, packed(2))] => StructUnionRepr::Compound(C.into(), Some(Packed(nz(2)).into()))); 793 test!(#[repr(C, align(1))] => StructUnionRepr::Compound(C.into(), Some(Align(nz(1)).into()))); 794 test!(#[repr(C, align(2))] => StructUnionRepr::Compound(C.into(), Some(Align(nz(2)).into()))); 795 test!(#[r#repr(r#C, r#align(2))] => StructUnionRepr::Compound(C.into(), Some(Align(nz(2)).into()))); 796 test!(#[r#repr(r#C, r#packed(2))] => StructUnionRepr::Compound(C.into(), Some(Packed(nz(2)).into()))); 797 798 test!(#[repr(transparent)] => EnumRepr::Transparent(s())); 799 test!(#[repr()] => EnumRepr::Compound(Rust.into(), None)); 800 test!(#[repr(align(1))] => EnumRepr::Compound(Rust.into(), Some(Align(nz(1)).into()))); 801 test!(#[repr(align(2))] => EnumRepr::Compound(Rust.into(), Some(Align(nz(2)).into()))); 802 803 macro_rules! for_each_compound_repr { 804 ($($r:tt => $var:expr),*) => { 805 $( 806 test!(#[repr($r)] => EnumRepr::Compound($var.into(), None)); 807 test!(#[repr($r, align(1))] => EnumRepr::Compound($var.into(), Some(Align(nz(1)).into()))); 808 test!(#[repr($r, align(2))] => EnumRepr::Compound($var.into(), Some(Align(nz(2)).into()))); 809 )* 810 } 811 } 812 813 for_each_compound_repr!( 814 C => C, 815 u8 => Primitive(U8), 816 u16 => Primitive(U16), 817 u32 => Primitive(U32), 818 u64 => Primitive(U64), 819 usize => Primitive(Usize), 820 i8 => Primitive(I8), 821 i16 => Primitive(I16), 822 i32 => Primitive(I32), 823 i64 => Primitive(I64), 824 isize => Primitive(Isize) 825 ); 826 827 use FromAttrsError::*; 828 use FromRawReprsError::*; 829 830 // Run failure tests which are valid for both `StructUnionRepr` and 831 // `EnumRepr`. 832 macro_rules! for_each_repr_type { 833 ($($repr:ident),*) => { 834 $( 835 // Invalid packed or align attributes 836 test!(@error #[repr(packed(0))] => $repr => Unrecognized.into()); 837 test!(@error #[repr(packed(3))] => $repr => Unrecognized.into()); 838 test!(@error #[repr(align(0))] => $repr => Unrecognized.into()); 839 test!(@error #[repr(align(3))] => $repr => Unrecognized.into()); 840 841 // Conflicts 842 test!(@error #[repr(transparent, transparent)] => $repr => FromRawReprs(Conflict).into()); 843 test!(@error #[repr(transparent, C)] => $repr => FromRawReprs(Conflict).into()); 844 test!(@error #[repr(transparent, Rust)] => $repr => FromRawReprs(Conflict).into()); 845 846 test!(@error #[repr(C, transparent)] => $repr => FromRawReprs(Conflict).into()); 847 test!(@error #[repr(C, C)] => $repr => FromRawReprs(Conflict).into()); 848 test!(@error #[repr(C, Rust)] => $repr => FromRawReprs(Conflict).into()); 849 850 test!(@error #[repr(Rust, transparent)] => $repr => FromRawReprs(Conflict).into()); 851 test!(@error #[repr(Rust, C)] => $repr => FromRawReprs(Conflict).into()); 852 test!(@error #[repr(Rust, Rust)] => $repr => FromRawReprs(Conflict).into()); 853 )* 854 } 855 } 856 857 for_each_repr_type!(StructUnionRepr, EnumRepr); 858 859 // Enum-specific conflicts. 860 // 861 // We don't bother to test every combination since that would be a huge 862 // number (enums can have primitive reprs u8, u16, u32, u64, usize, i8, 863 // i16, i32, i64, and isize). Instead, since the conflict logic doesn't 864 // care what specific value of `PrimitiveRepr` is present, we assume 865 // that testing against u8 alone is fine. 866 test!(@error #[repr(transparent, u8)] => EnumRepr => FromRawReprs(Conflict).into()); 867 test!(@error #[repr(u8, transparent)] => EnumRepr => FromRawReprs(Conflict).into()); 868 test!(@error #[repr(C, u8)] => EnumRepr => FromRawReprs(Conflict).into()); 869 test!(@error #[repr(u8, C)] => EnumRepr => FromRawReprs(Conflict).into()); 870 test!(@error #[repr(Rust, u8)] => EnumRepr => FromRawReprs(Conflict).into()); 871 test!(@error #[repr(u8, Rust)] => EnumRepr => FromRawReprs(Conflict).into()); 872 test!(@error #[repr(u8, u8)] => EnumRepr => FromRawReprs(Conflict).into()); 873 874 // Illegal struct/union reprs 875 test!(@error #[repr(u8)] => StructUnionRepr => FromRawReprs(Single(UnsupportedReprError)).into()); 876 test!(@error #[repr(u16)] => StructUnionRepr => FromRawReprs(Single(UnsupportedReprError)).into()); 877 test!(@error #[repr(u32)] => StructUnionRepr => FromRawReprs(Single(UnsupportedReprError)).into()); 878 test!(@error #[repr(u64)] => StructUnionRepr => FromRawReprs(Single(UnsupportedReprError)).into()); 879 test!(@error #[repr(usize)] => StructUnionRepr => FromRawReprs(Single(UnsupportedReprError)).into()); 880 test!(@error #[repr(i8)] => StructUnionRepr => FromRawReprs(Single(UnsupportedReprError)).into()); 881 test!(@error #[repr(i16)] => StructUnionRepr => FromRawReprs(Single(UnsupportedReprError)).into()); 882 test!(@error #[repr(i32)] => StructUnionRepr => FromRawReprs(Single(UnsupportedReprError)).into()); 883 test!(@error #[repr(i64)] => StructUnionRepr => FromRawReprs(Single(UnsupportedReprError)).into()); 884 test!(@error #[repr(isize)] => StructUnionRepr => FromRawReprs(Single(UnsupportedReprError)).into()); 885 886 // Illegal enum reprs 887 test!(@error #[repr(packed)] => EnumRepr => FromRawReprs(Single(UnsupportedReprError)).into()); 888 test!(@error #[repr(packed(1))] => EnumRepr => FromRawReprs(Single(UnsupportedReprError)).into()); 889 test!(@error #[repr(packed(2))] => EnumRepr => FromRawReprs(Single(UnsupportedReprError)).into()); 890 } 891 } 892