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