| 1 | use proc_macro2::{Delimiter, Group, Span, TokenStream}; |
| 2 | use quote::{format_ident, quote, quote_spanned, ToTokens}; |
| 3 | use syn::{ |
| 4 | parse_quote, punctuated::Punctuated, token, visit_mut::VisitMut, Attribute, Data, DataEnum, |
| 5 | DeriveInput, Error, Field, Fields, FieldsNamed, FieldsUnnamed, Generics, Ident, Index, |
| 6 | Lifetime, LifetimeParam, Meta, Result, Token, Type, Variant, Visibility, WhereClause, |
| 7 | }; |
| 8 | |
| 9 | use super::{ |
| 10 | args::{parse_args, Args, ProjReplace, UnpinImpl}, |
| 11 | PIN, |
| 12 | }; |
| 13 | use crate::utils::{ |
| 14 | determine_lifetime_name, determine_visibility, insert_lifetime_and_bound, ReplaceReceiver, |
| 15 | SliceExt, Variants, |
| 16 | }; |
| 17 | |
| 18 | pub(super) fn parse_derive(input: TokenStream) -> Result<TokenStream> { |
| 19 | let mut input: DeriveInput = syn::parse2(input)?; |
| 20 | |
| 21 | let mut cx; |
| 22 | let mut generate = GenerateTokens::default(); |
| 23 | |
| 24 | let ident = &input.ident; |
| 25 | let ty_generics = input.generics.split_for_impl().1; |
| 26 | let self_ty = parse_quote!(#ident #ty_generics); |
| 27 | let mut visitor = ReplaceReceiver(&self_ty); |
| 28 | visitor.visit_generics_mut(&mut input.generics); |
| 29 | visitor.visit_data_mut(&mut input.data); |
| 30 | |
| 31 | match &input.data { |
| 32 | Data::Struct(data) => { |
| 33 | cx = Context::new(&input.attrs, &input.vis, ident, &mut input.generics, Struct)?; |
| 34 | parse_struct(&mut cx, &data.fields, &mut generate)?; |
| 35 | } |
| 36 | Data::Enum(data) => { |
| 37 | cx = Context::new(&input.attrs, &input.vis, ident, &mut input.generics, Enum)?; |
| 38 | parse_enum(&mut cx, data, &mut generate)?; |
| 39 | } |
| 40 | Data::Union(_) => { |
| 41 | bail!(input, "#[pin_project] attribute may only be used on structs or enums" ); |
| 42 | } |
| 43 | } |
| 44 | |
| 45 | Ok(generate.into_tokens(&cx)) |
| 46 | } |
| 47 | |
| 48 | #[derive(Default)] |
| 49 | struct GenerateTokens { |
| 50 | exposed: TokenStream, |
| 51 | scoped: TokenStream, |
| 52 | } |
| 53 | |
| 54 | impl GenerateTokens { |
| 55 | fn extend(&mut self, expose: bool, tokens: TokenStream) { |
| 56 | if expose { |
| 57 | self.exposed.extend(tokens); |
| 58 | } else { |
| 59 | self.scoped.extend(tokens); |
| 60 | } |
| 61 | } |
| 62 | |
| 63 | fn into_tokens(self, cx: &Context<'_>) -> TokenStream { |
| 64 | let mut tokens = self.exposed; |
| 65 | let scoped = self.scoped; |
| 66 | |
| 67 | let unpin_impl = make_unpin_impl(cx); |
| 68 | let drop_impl = make_drop_impl(cx); |
| 69 | let allowed_lints = global_allowed_lints(); |
| 70 | |
| 71 | tokens.extend(quote! { |
| 72 | // All items except projected types are generated inside a `const` scope. |
| 73 | // This makes it impossible for user code to refer to these types. |
| 74 | // However, this prevents Rustdoc from displaying docs for any |
| 75 | // of our types. In particular, users cannot see the |
| 76 | // automatically generated `Unpin` impl for the '__UnpinStruct' types |
| 77 | // |
| 78 | // Previously, we provided a flag to correctly document the |
| 79 | // automatically generated `Unpin` impl by using def-site hygiene, |
| 80 | // but it is now removed. |
| 81 | // |
| 82 | // Refs: |
| 83 | // - https://github.com/rust-lang/rust/issues/63281 |
| 84 | // - https://github.com/taiki-e/pin-project/pull/53#issuecomment-525906867 |
| 85 | // - https://github.com/taiki-e/pin-project/pull/70 |
| 86 | #allowed_lints |
| 87 | #[allow(unused_qualifications)] |
| 88 | #[allow(clippy::semicolon_if_nothing_returned)] |
| 89 | #[allow(clippy::use_self)] |
| 90 | #[allow(clippy::used_underscore_binding)] |
| 91 | const _: () = { |
| 92 | #[allow(unused_extern_crates)] |
| 93 | extern crate pin_project as _pin_project; |
| 94 | #scoped |
| 95 | #unpin_impl |
| 96 | #drop_impl |
| 97 | }; |
| 98 | }); |
| 99 | tokens |
| 100 | } |
| 101 | } |
| 102 | |
| 103 | /// Returns attributes that should be applied to all generated code. |
| 104 | fn global_allowed_lints() -> TokenStream { |
| 105 | quote! { |
| 106 | #[allow(box_pointers)] // This lint warns use of the `Box` type. |
| 107 | #[allow(deprecated)] |
| 108 | #[allow(explicit_outlives_requirements)] // https://github.com/rust-lang/rust/issues/60993 |
| 109 | #[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058 |
| 110 | #[allow(unreachable_pub)] // This lint warns `pub` field in private struct. |
| 111 | #[allow(unused_tuple_struct_fields)] |
| 112 | // This lint warns of `clippy::*` generated by external macros. |
| 113 | // We allow this lint for compatibility with older compilers. |
| 114 | #[allow(clippy::unknown_clippy_lints)] |
| 115 | #[allow(clippy::pattern_type_mismatch)] |
| 116 | #[allow(clippy::redundant_pub_crate)] // This lint warns `pub(crate)` field in private struct. |
| 117 | #[allow(clippy::type_repetition_in_bounds)] // https://github.com/rust-lang/rust-clippy/issues/4326 |
| 118 | } |
| 119 | } |
| 120 | |
| 121 | /// Returns attributes used on projected types. |
| 122 | fn proj_allowed_lints(cx: &Context<'_>) -> (TokenStream, TokenStream, TokenStream) { |
| 123 | let large_enum_variant = if cx.kind == Enum { |
| 124 | Some(quote! { |
| 125 | #[allow(variant_size_differences)] |
| 126 | #[allow(clippy::large_enum_variant)] |
| 127 | }) |
| 128 | } else { |
| 129 | None |
| 130 | }; |
| 131 | let global_allowed_lints = global_allowed_lints(); |
| 132 | let proj_mut_allowed_lints = if cx.project { Some(&global_allowed_lints) } else { None }; |
| 133 | let proj_mut = quote! { |
| 134 | #proj_mut_allowed_lints |
| 135 | #[allow(dead_code)] // This lint warns unused fields/variants. |
| 136 | #[allow(clippy::mut_mut)] // This lint warns `&mut &mut <ty>`. |
| 137 | }; |
| 138 | let proj_ref_allowed_lints = if cx.project_ref { Some(&global_allowed_lints) } else { None }; |
| 139 | let proj_ref = quote! { |
| 140 | #proj_ref_allowed_lints |
| 141 | #[allow(dead_code)] // This lint warns unused fields/variants. |
| 142 | #[allow(clippy::ref_option_ref)] // This lint warns `&Option<&<ty>>`. |
| 143 | }; |
| 144 | let proj_own_allowed_lints = |
| 145 | if cx.project_replace.ident().is_some() { Some(&global_allowed_lints) } else { None }; |
| 146 | let proj_own = quote! { |
| 147 | #proj_own_allowed_lints |
| 148 | #[allow(dead_code)] // This lint warns unused fields/variants. |
| 149 | #large_enum_variant |
| 150 | }; |
| 151 | (proj_mut, proj_ref, proj_own) |
| 152 | } |
| 153 | |
| 154 | struct Context<'a> { |
| 155 | /// The original type. |
| 156 | orig: OriginalType<'a>, |
| 157 | /// The projected types. |
| 158 | proj: ProjectedType, |
| 159 | /// Types of the pinned fields. |
| 160 | pinned_fields: Vec<&'a Type>, |
| 161 | /// Kind of the original type: struct or enum |
| 162 | kind: TypeKind, |
| 163 | |
| 164 | /// `PinnedDrop` argument. |
| 165 | pinned_drop: Option<Span>, |
| 166 | /// `UnsafeUnpin` or `!Unpin` argument. |
| 167 | unpin_impl: UnpinImpl, |
| 168 | /// `project` argument. |
| 169 | project: bool, |
| 170 | /// `project_ref` argument. |
| 171 | project_ref: bool, |
| 172 | /// `project_replace [= <ident>]` argument. |
| 173 | project_replace: ProjReplace, |
| 174 | } |
| 175 | |
| 176 | impl<'a> Context<'a> { |
| 177 | fn new( |
| 178 | attrs: &'a [Attribute], |
| 179 | vis: &'a Visibility, |
| 180 | ident: &'a Ident, |
| 181 | generics: &'a mut Generics, |
| 182 | kind: TypeKind, |
| 183 | ) -> Result<Self> { |
| 184 | let Args { pinned_drop, unpin_impl, project, project_ref, project_replace } = |
| 185 | parse_args(attrs)?; |
| 186 | |
| 187 | if let Some(name) = [project.as_ref(), project_ref.as_ref(), project_replace.ident()] |
| 188 | .iter() |
| 189 | .filter_map(Option::as_ref) |
| 190 | .find(|name| **name == ident) |
| 191 | { |
| 192 | bail!(name, "name `{}` is the same as the original type name" , name); |
| 193 | } |
| 194 | |
| 195 | let mut lifetime_name = String::from("'pin" ); |
| 196 | determine_lifetime_name(&mut lifetime_name, generics); |
| 197 | let lifetime = Lifetime::new(&lifetime_name, Span::call_site()); |
| 198 | |
| 199 | let ty_generics = generics.split_for_impl().1; |
| 200 | let ty_generics_as_generics = parse_quote!(#ty_generics); |
| 201 | let mut proj_generics = generics.clone(); |
| 202 | let pred = insert_lifetime_and_bound( |
| 203 | &mut proj_generics, |
| 204 | lifetime.clone(), |
| 205 | &ty_generics_as_generics, |
| 206 | ident, |
| 207 | ); |
| 208 | let mut where_clause = generics.make_where_clause().clone(); |
| 209 | where_clause.predicates.push(pred); |
| 210 | |
| 211 | let own_ident = project_replace |
| 212 | .ident() |
| 213 | .cloned() |
| 214 | .unwrap_or_else(|| format_ident!("__{}ProjectionOwned" , ident)); |
| 215 | |
| 216 | Ok(Self { |
| 217 | kind, |
| 218 | pinned_drop, |
| 219 | unpin_impl, |
| 220 | project: project.is_some(), |
| 221 | project_ref: project_ref.is_some(), |
| 222 | project_replace, |
| 223 | proj: ProjectedType { |
| 224 | vis: determine_visibility(vis), |
| 225 | mut_ident: project.unwrap_or_else(|| format_ident!("__{}Projection" , ident)), |
| 226 | ref_ident: project_ref.unwrap_or_else(|| format_ident!("__{}ProjectionRef" , ident)), |
| 227 | own_ident, |
| 228 | lifetime, |
| 229 | generics: proj_generics, |
| 230 | where_clause, |
| 231 | }, |
| 232 | orig: OriginalType { attrs, vis, ident, generics }, |
| 233 | pinned_fields: Vec::new(), |
| 234 | }) |
| 235 | } |
| 236 | } |
| 237 | |
| 238 | #[derive(Copy, Clone, PartialEq)] |
| 239 | enum TypeKind { |
| 240 | Enum, |
| 241 | Struct, |
| 242 | } |
| 243 | |
| 244 | use TypeKind::{Enum, Struct}; |
| 245 | |
| 246 | struct OriginalType<'a> { |
| 247 | /// Attributes of the original type. |
| 248 | attrs: &'a [Attribute], |
| 249 | /// Visibility of the original type. |
| 250 | vis: &'a Visibility, |
| 251 | /// Name of the original type. |
| 252 | ident: &'a Ident, |
| 253 | /// Generics of the original type. |
| 254 | generics: &'a Generics, |
| 255 | } |
| 256 | |
| 257 | struct ProjectedType { |
| 258 | /// Visibility of the projected types. |
| 259 | vis: Visibility, |
| 260 | /// Name of the projected type returned by `project` method. |
| 261 | mut_ident: Ident, |
| 262 | /// Name of the projected type returned by `project_ref` method. |
| 263 | ref_ident: Ident, |
| 264 | /// Name of the projected type returned by `project_replace` method. |
| 265 | own_ident: Ident, |
| 266 | /// Lifetime on the generated projected types. |
| 267 | lifetime: Lifetime, |
| 268 | /// Generics of the projected types. |
| 269 | generics: Generics, |
| 270 | /// `where` clause of the projected types. This has an additional |
| 271 | /// bound generated by `insert_lifetime_and_bound` |
| 272 | where_clause: WhereClause, |
| 273 | } |
| 274 | |
| 275 | struct ProjectedVariants { |
| 276 | proj_variants: TokenStream, |
| 277 | proj_ref_variants: TokenStream, |
| 278 | proj_own_variants: TokenStream, |
| 279 | proj_arms: TokenStream, |
| 280 | proj_ref_arms: TokenStream, |
| 281 | proj_own_arms: TokenStream, |
| 282 | } |
| 283 | |
| 284 | #[derive(Default)] |
| 285 | struct ProjectedFields { |
| 286 | proj_pat: TokenStream, |
| 287 | proj_body: TokenStream, |
| 288 | proj_own_body: TokenStream, |
| 289 | proj_fields: TokenStream, |
| 290 | proj_ref_fields: TokenStream, |
| 291 | proj_own_fields: TokenStream, |
| 292 | } |
| 293 | |
| 294 | fn validate_struct(ident: &Ident, fields: &Fields) -> Result<()> { |
| 295 | if fields.is_empty() { |
| 296 | let msg = "#[pin_project] attribute may not be used on structs with zero fields" ; |
| 297 | if let Fields::Unit = fields { |
| 298 | bail!(ident, msg) |
| 299 | } |
| 300 | bail!(fields, msg) |
| 301 | } |
| 302 | Ok(()) |
| 303 | } |
| 304 | |
| 305 | fn validate_enum(brace_token: token::Brace, variants: &Variants) -> Result<()> { |
| 306 | if variants.is_empty() { |
| 307 | return Err(Error::new( |
| 308 | brace_token.span.join(), |
| 309 | "#[pin_project] attribute may not be used on enums without variants" , |
| 310 | )); |
| 311 | } |
| 312 | let has_field = variants.iter().try_fold(false, |has_field, v| { |
| 313 | if let Some((_, e)) = &v.discriminant { |
| 314 | bail!(e, "#[pin_project] attribute may not be used on enums with discriminants" ); |
| 315 | } else if let Some(attr) = v.attrs.find(PIN) { |
| 316 | bail!(attr, "#[pin] attribute may only be used on fields of structs or variants" ); |
| 317 | } else if v.fields.is_empty() { |
| 318 | Ok(has_field) |
| 319 | } else { |
| 320 | Ok(true) |
| 321 | } |
| 322 | })?; |
| 323 | if has_field { |
| 324 | Ok(()) |
| 325 | } else { |
| 326 | bail!(variants, "#[pin_project] attribute may not be used on enums with zero fields" ); |
| 327 | } |
| 328 | } |
| 329 | |
| 330 | fn parse_struct<'a>( |
| 331 | cx: &mut Context<'a>, |
| 332 | fields: &'a Fields, |
| 333 | generate: &mut GenerateTokens, |
| 334 | ) -> Result<()> { |
| 335 | // Do this first for a better error message. |
| 336 | let packed_check = ensure_not_packed(&cx.orig, Some(fields))?; |
| 337 | |
| 338 | validate_struct(cx.orig.ident, fields)?; |
| 339 | |
| 340 | let ProjectedFields { |
| 341 | proj_pat, |
| 342 | proj_body, |
| 343 | proj_fields, |
| 344 | proj_ref_fields, |
| 345 | proj_own_fields, |
| 346 | proj_own_body, |
| 347 | } = match fields { |
| 348 | Fields::Named(_) => visit_fields(cx, None, fields, Delimiter::Brace)?, |
| 349 | Fields::Unnamed(_) => visit_fields(cx, None, fields, Delimiter::Parenthesis)?, |
| 350 | Fields::Unit => unreachable!(), |
| 351 | }; |
| 352 | |
| 353 | let proj_ident = &cx.proj.mut_ident; |
| 354 | let proj_ref_ident = &cx.proj.ref_ident; |
| 355 | let proj_own_ident = &cx.proj.own_ident; |
| 356 | let vis = &cx.proj.vis; |
| 357 | let mut orig_generics = cx.orig.generics.clone(); |
| 358 | let orig_where_clause = orig_generics.where_clause.take(); |
| 359 | let proj_generics = &cx.proj.generics; |
| 360 | let proj_where_clause = &cx.proj.where_clause; |
| 361 | |
| 362 | // For tuple structs, we need to generate `(T1, T2) where Foo: Bar` |
| 363 | // For non-tuple structs, we need to generate `where Foo: Bar { field1: T }` |
| 364 | let (where_clause_fields, where_clause_ref_fields, where_clause_own_fields) = match fields { |
| 365 | Fields::Named(_) => ( |
| 366 | quote!(#proj_where_clause #proj_fields), |
| 367 | quote!(#proj_where_clause #proj_ref_fields), |
| 368 | quote!(#orig_where_clause #proj_own_fields), |
| 369 | ), |
| 370 | Fields::Unnamed(_) => ( |
| 371 | quote!(#proj_fields #proj_where_clause;), |
| 372 | quote!(#proj_ref_fields #proj_where_clause;), |
| 373 | quote!(#proj_own_fields #orig_where_clause;), |
| 374 | ), |
| 375 | Fields::Unit => unreachable!(), |
| 376 | }; |
| 377 | |
| 378 | let (proj_attrs, proj_ref_attrs, proj_own_attrs) = proj_allowed_lints(cx); |
| 379 | generate.extend(cx.project, quote! { |
| 380 | #proj_attrs |
| 381 | #vis struct #proj_ident #proj_generics #where_clause_fields |
| 382 | }); |
| 383 | generate.extend(cx.project_ref, quote! { |
| 384 | #proj_ref_attrs |
| 385 | #vis struct #proj_ref_ident #proj_generics #where_clause_ref_fields |
| 386 | }); |
| 387 | if cx.project_replace.span().is_some() { |
| 388 | generate.extend(cx.project_replace.ident().is_some(), quote! { |
| 389 | #proj_own_attrs |
| 390 | #vis struct #proj_own_ident #orig_generics #where_clause_own_fields |
| 391 | }); |
| 392 | } |
| 393 | |
| 394 | let proj_mut_body = quote! { |
| 395 | let Self #proj_pat = self.get_unchecked_mut(); |
| 396 | #proj_ident #proj_body |
| 397 | }; |
| 398 | let proj_ref_body = quote! { |
| 399 | let Self #proj_pat = self.get_ref(); |
| 400 | #proj_ref_ident #proj_body |
| 401 | }; |
| 402 | let proj_own_body = quote! { |
| 403 | let Self #proj_pat = &mut *__self_ptr; |
| 404 | #proj_own_body |
| 405 | }; |
| 406 | generate.extend(false, make_proj_impl(cx, &proj_mut_body, &proj_ref_body, &proj_own_body)); |
| 407 | |
| 408 | generate.extend(false, packed_check); |
| 409 | Ok(()) |
| 410 | } |
| 411 | |
| 412 | fn parse_enum<'a>( |
| 413 | cx: &mut Context<'a>, |
| 414 | DataEnum { brace_token, variants, .. }: &'a DataEnum, |
| 415 | generate: &mut GenerateTokens, |
| 416 | ) -> Result<()> { |
| 417 | if let ProjReplace::Unnamed { span } = &cx.project_replace { |
| 418 | return Err(Error::new( |
| 419 | *span, |
| 420 | "`project_replace` argument requires a value when used on enums" , |
| 421 | )); |
| 422 | } |
| 423 | |
| 424 | // #[repr(packed)] cannot be apply on enums and will be rejected by rustc. |
| 425 | // However, we should not rely on the behavior of rustc that rejects this. |
| 426 | // https://github.com/taiki-e/pin-project/pull/324#discussion_r612388001 |
| 427 | // |
| 428 | // Do this first for a better error message. |
| 429 | ensure_not_packed(&cx.orig, None)?; |
| 430 | |
| 431 | validate_enum(*brace_token, variants)?; |
| 432 | |
| 433 | let ProjectedVariants { |
| 434 | proj_variants, |
| 435 | proj_ref_variants, |
| 436 | proj_own_variants, |
| 437 | proj_arms, |
| 438 | proj_ref_arms, |
| 439 | proj_own_arms, |
| 440 | } = visit_variants(cx, variants)?; |
| 441 | |
| 442 | let proj_ident = &cx.proj.mut_ident; |
| 443 | let proj_ref_ident = &cx.proj.ref_ident; |
| 444 | let proj_own_ident = &cx.proj.own_ident; |
| 445 | let vis = &cx.proj.vis; |
| 446 | let mut orig_generics = cx.orig.generics.clone(); |
| 447 | let orig_where_clause = orig_generics.where_clause.take(); |
| 448 | let proj_generics = &cx.proj.generics; |
| 449 | let proj_where_clause = &cx.proj.where_clause; |
| 450 | |
| 451 | let (proj_attrs, proj_ref_attrs, proj_own_attrs) = proj_allowed_lints(cx); |
| 452 | if cx.project { |
| 453 | generate.extend(true, quote! { |
| 454 | #proj_attrs |
| 455 | #vis enum #proj_ident #proj_generics #proj_where_clause { |
| 456 | #proj_variants |
| 457 | } |
| 458 | }); |
| 459 | } |
| 460 | if cx.project_ref { |
| 461 | generate.extend(true, quote! { |
| 462 | #proj_ref_attrs |
| 463 | #vis enum #proj_ref_ident #proj_generics #proj_where_clause { |
| 464 | #proj_ref_variants |
| 465 | } |
| 466 | }); |
| 467 | } |
| 468 | if cx.project_replace.ident().is_some() { |
| 469 | generate.extend(true, quote! { |
| 470 | #proj_own_attrs |
| 471 | #vis enum #proj_own_ident #orig_generics #orig_where_clause { |
| 472 | #proj_own_variants |
| 473 | } |
| 474 | }); |
| 475 | } |
| 476 | |
| 477 | let proj_mut_body = quote! { |
| 478 | match self.get_unchecked_mut() { |
| 479 | #proj_arms |
| 480 | } |
| 481 | }; |
| 482 | let proj_ref_body = quote! { |
| 483 | match self.get_ref() { |
| 484 | #proj_ref_arms |
| 485 | } |
| 486 | }; |
| 487 | let proj_own_body = quote! { |
| 488 | match &mut *__self_ptr { |
| 489 | #proj_own_arms |
| 490 | } |
| 491 | }; |
| 492 | generate.extend(false, make_proj_impl(cx, &proj_mut_body, &proj_ref_body, &proj_own_body)); |
| 493 | |
| 494 | Ok(()) |
| 495 | } |
| 496 | |
| 497 | fn visit_variants<'a>(cx: &mut Context<'a>, variants: &'a Variants) -> Result<ProjectedVariants> { |
| 498 | let mut proj_variants = TokenStream::new(); |
| 499 | let mut proj_ref_variants = TokenStream::new(); |
| 500 | let mut proj_own_variants = TokenStream::new(); |
| 501 | let mut proj_arms = TokenStream::new(); |
| 502 | let mut proj_ref_arms = TokenStream::new(); |
| 503 | let mut proj_own_arms = TokenStream::new(); |
| 504 | |
| 505 | for Variant { ident, fields, .. } in variants { |
| 506 | let ProjectedFields { |
| 507 | proj_pat, |
| 508 | proj_body, |
| 509 | proj_fields, |
| 510 | proj_ref_fields, |
| 511 | proj_own_fields, |
| 512 | proj_own_body, |
| 513 | } = match fields { |
| 514 | Fields::Named(_) => visit_fields(cx, Some(ident), fields, Delimiter::Brace)?, |
| 515 | Fields::Unnamed(_) => visit_fields(cx, Some(ident), fields, Delimiter::Parenthesis)?, |
| 516 | Fields::Unit => ProjectedFields { |
| 517 | proj_own_body: proj_own_body(cx, Some(ident), None, &[]), |
| 518 | ..Default::default() |
| 519 | }, |
| 520 | }; |
| 521 | |
| 522 | let proj_ident = &cx.proj.mut_ident; |
| 523 | let proj_ref_ident = &cx.proj.ref_ident; |
| 524 | proj_variants.extend(quote! { |
| 525 | #ident #proj_fields, |
| 526 | }); |
| 527 | proj_ref_variants.extend(quote! { |
| 528 | #ident #proj_ref_fields, |
| 529 | }); |
| 530 | proj_own_variants.extend(quote! { |
| 531 | #ident #proj_own_fields, |
| 532 | }); |
| 533 | proj_arms.extend(quote! { |
| 534 | Self::#ident #proj_pat => #proj_ident::#ident #proj_body, |
| 535 | }); |
| 536 | proj_ref_arms.extend(quote! { |
| 537 | Self::#ident #proj_pat => #proj_ref_ident::#ident #proj_body, |
| 538 | }); |
| 539 | proj_own_arms.extend(quote! { |
| 540 | Self::#ident #proj_pat => { #proj_own_body } |
| 541 | }); |
| 542 | } |
| 543 | |
| 544 | Ok(ProjectedVariants { |
| 545 | proj_variants, |
| 546 | proj_ref_variants, |
| 547 | proj_own_variants, |
| 548 | proj_arms, |
| 549 | proj_ref_arms, |
| 550 | proj_own_arms, |
| 551 | }) |
| 552 | } |
| 553 | |
| 554 | fn visit_fields<'a>( |
| 555 | cx: &mut Context<'a>, |
| 556 | variant_ident: Option<&Ident>, |
| 557 | fields: &'a Fields, |
| 558 | delim: Delimiter, |
| 559 | ) -> Result<ProjectedFields> { |
| 560 | fn surround(delim: Delimiter, tokens: TokenStream) -> TokenStream { |
| 561 | Group::new(delim, tokens).into_token_stream() |
| 562 | } |
| 563 | |
| 564 | let mut proj_pat = TokenStream::new(); |
| 565 | let mut proj_body = TokenStream::new(); |
| 566 | let mut proj_fields = TokenStream::new(); |
| 567 | let mut proj_ref_fields = TokenStream::new(); |
| 568 | let mut proj_own_fields = TokenStream::new(); |
| 569 | let mut proj_move = TokenStream::new(); |
| 570 | let mut pinned_bindings = Vec::with_capacity(fields.len()); |
| 571 | |
| 572 | for (i, Field { attrs, vis, ident, colon_token, ty, mutability: _ }) in |
| 573 | fields.iter().enumerate() |
| 574 | { |
| 575 | let binding = ident.clone().unwrap_or_else(|| format_ident!("_{}" , i)); |
| 576 | proj_pat.extend(quote!(#binding,)); |
| 577 | let lifetime = &cx.proj.lifetime; |
| 578 | if attrs.position_exact(PIN)?.is_some() { |
| 579 | proj_fields.extend(quote! { |
| 580 | #vis #ident #colon_token ::pin_project::__private::Pin<lifetime mut (#ty)>, |
| 581 | }); |
| 582 | proj_ref_fields.extend(quote! { |
| 583 | #vis #ident #colon_token ::pin_project::__private::Pin<lifetime (#ty)>, |
| 584 | }); |
| 585 | proj_own_fields.extend(quote! { |
| 586 | #vis #ident #colon_token ::pin_project::__private::PhantomData<#ty>, |
| 587 | }); |
| 588 | proj_body.extend(quote! { |
| 589 | #ident #colon_token _pin_project::__private::Pin::new_unchecked(#binding), |
| 590 | }); |
| 591 | proj_move.extend(quote! { |
| 592 | #ident #colon_token _pin_project::__private::PhantomData, |
| 593 | }); |
| 594 | |
| 595 | cx.pinned_fields.push(ty); |
| 596 | pinned_bindings.push(binding); |
| 597 | } else { |
| 598 | proj_fields.extend(quote! { |
| 599 | #vis #ident #colon_token lifetime mut (#ty), |
| 600 | }); |
| 601 | proj_ref_fields.extend(quote! { |
| 602 | #vis #ident #colon_token lifetime (#ty), |
| 603 | }); |
| 604 | proj_own_fields.extend(quote! { |
| 605 | #vis #ident #colon_token #ty, |
| 606 | }); |
| 607 | proj_body.extend(quote! { |
| 608 | #binding, |
| 609 | }); |
| 610 | proj_move.extend(quote! { |
| 611 | #ident #colon_token _pin_project::__private::ptr::read(#binding), |
| 612 | }); |
| 613 | } |
| 614 | } |
| 615 | |
| 616 | let proj_pat = surround(delim, proj_pat); |
| 617 | let proj_body = surround(delim, proj_body); |
| 618 | let proj_fields = surround(delim, proj_fields); |
| 619 | let proj_ref_fields = surround(delim, proj_ref_fields); |
| 620 | let proj_own_fields = surround(delim, proj_own_fields); |
| 621 | |
| 622 | let proj_move = Group::new(delim, proj_move); |
| 623 | let proj_own_body = proj_own_body(cx, variant_ident, Some(&proj_move), &pinned_bindings); |
| 624 | |
| 625 | Ok(ProjectedFields { |
| 626 | proj_pat, |
| 627 | proj_body, |
| 628 | proj_own_body, |
| 629 | proj_fields, |
| 630 | proj_ref_fields, |
| 631 | proj_own_fields, |
| 632 | }) |
| 633 | } |
| 634 | |
| 635 | /// Generates the processing that `project_replace` does for the struct or each variant. |
| 636 | /// |
| 637 | /// Note: `pinned_fields` must be in declaration order. |
| 638 | fn proj_own_body( |
| 639 | cx: &Context<'_>, |
| 640 | variant_ident: Option<&Ident>, |
| 641 | proj_move: Option<&Group>, |
| 642 | pinned_fields: &[Ident], |
| 643 | ) -> TokenStream { |
| 644 | let ident = &cx.proj.own_ident; |
| 645 | let proj_own = match variant_ident { |
| 646 | Some(variant_ident) => quote!(#ident::#variant_ident), |
| 647 | None => quote!(#ident), |
| 648 | }; |
| 649 | |
| 650 | // The fields of the struct and the active enum variant are dropped |
| 651 | // in declaration order. |
| 652 | // Refs: https://doc.rust-lang.org/reference/destructors.html |
| 653 | let pinned_fields = pinned_fields.iter().rev(); |
| 654 | |
| 655 | quote! { |
| 656 | // First, extract all the unpinned fields. |
| 657 | let __result = #proj_own #proj_move; |
| 658 | |
| 659 | // Now create guards to drop all the pinned fields. |
| 660 | // |
| 661 | // Due to a compiler bug (https://github.com/rust-lang/rust/issues/47949) |
| 662 | // this must be in its own scope, or else `__result` will not be dropped |
| 663 | // if any of the destructors panic. |
| 664 | { |
| 665 | #( |
| 666 | let __guard = _pin_project::__private::UnsafeDropInPlaceGuard::new(#pinned_fields); |
| 667 | )* |
| 668 | } |
| 669 | |
| 670 | // Finally, return the result. |
| 671 | __result |
| 672 | } |
| 673 | } |
| 674 | |
| 675 | /// Creates `Unpin` implementation for the original type. |
| 676 | /// |
| 677 | /// The kind of `Unpin` impl generated depends on `unpin_impl` field: |
| 678 | /// - `UnpinImpl::Unsafe` - Implements `Unpin` via `UnsafeUnpin` impl. |
| 679 | /// - `UnpinImpl::Negative` - Generates `Unpin` impl with bounds that will never be true. |
| 680 | /// - `UnpinImpl::Default` - Generates `Unpin` impl that requires `Unpin` for all pinned fields. |
| 681 | fn make_unpin_impl(cx: &Context<'_>) -> TokenStream { |
| 682 | match cx.unpin_impl { |
| 683 | UnpinImpl::Unsafe(span) => { |
| 684 | let mut proj_generics = cx.proj.generics.clone(); |
| 685 | let orig_ident = cx.orig.ident; |
| 686 | let lifetime = &cx.proj.lifetime; |
| 687 | |
| 688 | // Make the error message highlight `UnsafeUnpin` argument. |
| 689 | proj_generics.make_where_clause().predicates.push(parse_quote_spanned! { span => |
| 690 | _pin_project::__private::Wrapper<#lifetime, Self>: _pin_project::UnsafeUnpin |
| 691 | }); |
| 692 | |
| 693 | let (impl_generics, _, where_clause) = proj_generics.split_for_impl(); |
| 694 | let ty_generics = cx.orig.generics.split_for_impl().1; |
| 695 | |
| 696 | quote_spanned! { span => |
| 697 | impl #impl_generics _pin_project::__private::Unpin for #orig_ident #ty_generics |
| 698 | #where_clause |
| 699 | { |
| 700 | } |
| 701 | } |
| 702 | } |
| 703 | UnpinImpl::Negative(span) => { |
| 704 | let mut proj_generics = cx.proj.generics.clone(); |
| 705 | let orig_ident = cx.orig.ident; |
| 706 | let lifetime = &cx.proj.lifetime; |
| 707 | |
| 708 | proj_generics.make_where_clause().predicates.push(parse_quote! { |
| 709 | _pin_project::__private::Wrapper< |
| 710 | #lifetime, _pin_project::__private::PhantomPinned |
| 711 | >: _pin_project::__private::Unpin |
| 712 | }); |
| 713 | |
| 714 | let (proj_impl_generics, _, proj_where_clause) = proj_generics.split_for_impl(); |
| 715 | let ty_generics = cx.orig.generics.split_for_impl().1; |
| 716 | |
| 717 | // For interoperability with `forbid(unsafe_code)`, `unsafe` token should be |
| 718 | // call-site span. |
| 719 | let unsafety = <Token![unsafe]>::default(); |
| 720 | quote_spanned! { span => |
| 721 | #[doc(hidden)] |
| 722 | impl #proj_impl_generics _pin_project::__private::Unpin |
| 723 | for #orig_ident #ty_generics |
| 724 | #proj_where_clause |
| 725 | { |
| 726 | } |
| 727 | |
| 728 | // Generate a dummy impl of `UnsafeUnpin`, to ensure that the user cannot implement it. |
| 729 | // |
| 730 | // To ensure that users don't accidentally write a non-functional `UnsafeUnpin` |
| 731 | // impls, we emit one ourselves. If the user ends up writing an `UnsafeUnpin` |
| 732 | // impl, they'll get a "conflicting implementations of trait" error when |
| 733 | // coherence checks are run. |
| 734 | #[doc(hidden)] |
| 735 | #unsafety impl #proj_impl_generics _pin_project::UnsafeUnpin |
| 736 | for #orig_ident #ty_generics |
| 737 | #proj_where_clause |
| 738 | { |
| 739 | } |
| 740 | } |
| 741 | } |
| 742 | UnpinImpl::Default => { |
| 743 | let mut full_where_clause = cx.orig.generics.where_clause.clone().unwrap(); |
| 744 | |
| 745 | // Generate a field in our new struct for every |
| 746 | // pinned field in the original type. |
| 747 | let fields = cx.pinned_fields.iter().enumerate().map(|(i, ty)| { |
| 748 | let field_ident = format_ident!("__field{}" , i); |
| 749 | quote!(#field_ident: #ty) |
| 750 | }); |
| 751 | |
| 752 | // We could try to determine the subset of type parameters |
| 753 | // and lifetimes that are actually used by the pinned fields |
| 754 | // (as opposed to those only used by unpinned fields). |
| 755 | // However, this would be tricky and error-prone, since |
| 756 | // it's possible for users to create types that would alias |
| 757 | // with generic parameters (e.g. 'struct T'). |
| 758 | // |
| 759 | // Instead, we generate a use of every single type parameter |
| 760 | // and lifetime used in the original struct. For type parameters, |
| 761 | // we generate code like this: |
| 762 | // |
| 763 | // ```rust |
| 764 | // struct AlwaysUnpin<T: ?Sized>(PhantomData<T>) {} |
| 765 | // impl<T: ?Sized> Unpin for AlwaysUnpin<T> {} |
| 766 | // |
| 767 | // ... |
| 768 | // _field: AlwaysUnpin<(A, B, C)> |
| 769 | // ``` |
| 770 | // |
| 771 | // This ensures that any unused type parameters |
| 772 | // don't end up with `Unpin` bounds. |
| 773 | let lifetime_fields = cx.orig.generics.lifetimes().enumerate().map( |
| 774 | |(i, LifetimeParam { lifetime, .. })| { |
| 775 | let field_ident = format_ident!("__lifetime{}" , i); |
| 776 | quote!(#field_ident: lifetime ()) |
| 777 | }, |
| 778 | ); |
| 779 | |
| 780 | let orig_ident = cx.orig.ident; |
| 781 | let struct_ident = format_ident!("__{}" , orig_ident); |
| 782 | let vis = cx.orig.vis; |
| 783 | let lifetime = &cx.proj.lifetime; |
| 784 | let type_params = cx.orig.generics.type_params().map(|t| &t.ident); |
| 785 | let proj_generics = &cx.proj.generics; |
| 786 | let (proj_impl_generics, proj_ty_generics, _) = proj_generics.split_for_impl(); |
| 787 | let (_, ty_generics, where_clause) = cx.orig.generics.split_for_impl(); |
| 788 | |
| 789 | full_where_clause.predicates.push(parse_quote! { |
| 790 | #struct_ident #proj_ty_generics: _pin_project::__private::Unpin |
| 791 | }); |
| 792 | |
| 793 | quote! { |
| 794 | // This needs to have the same visibility as the original type, |
| 795 | // due to the limitations of the 'public in private' error. |
| 796 | // |
| 797 | // Our goal is to implement the public trait `Unpin` for |
| 798 | // a potentially public user type. Because of this, rust |
| 799 | // requires that any types mentioned in the where clause of |
| 800 | // our `Unpin` impl also be public. This means that our generated |
| 801 | // `__UnpinStruct` type must also be public. |
| 802 | // However, we ensure that the user can never actually reference |
| 803 | // this 'public' type by creating this type in the inside of `const`. |
| 804 | #[allow(missing_debug_implementations)] |
| 805 | #vis struct #struct_ident #proj_generics #where_clause { |
| 806 | __pin_project_use_generics: _pin_project::__private::AlwaysUnpin< |
| 807 | #lifetime, (#(_pin_project::__private::PhantomData<#type_params>),*) |
| 808 | >, |
| 809 | |
| 810 | #(#fields,)* |
| 811 | #(#lifetime_fields,)* |
| 812 | } |
| 813 | |
| 814 | impl #proj_impl_generics _pin_project::__private::Unpin |
| 815 | for #orig_ident #ty_generics |
| 816 | #full_where_clause |
| 817 | { |
| 818 | } |
| 819 | |
| 820 | // Generate a dummy impl of `UnsafeUnpin`, to ensure that the user cannot implement it. |
| 821 | // |
| 822 | // To ensure that users don't accidentally write a non-functional `UnsafeUnpin` |
| 823 | // impls, we emit one ourselves. If the user ends up writing an `UnsafeUnpin` |
| 824 | // impl, they'll get a "conflicting implementations of trait" error when |
| 825 | // coherence checks are run. |
| 826 | #[doc(hidden)] |
| 827 | unsafe impl #proj_impl_generics _pin_project::UnsafeUnpin |
| 828 | for #orig_ident #ty_generics |
| 829 | #full_where_clause |
| 830 | { |
| 831 | } |
| 832 | } |
| 833 | } |
| 834 | } |
| 835 | } |
| 836 | |
| 837 | /// Creates `Drop` implementation for the original type. |
| 838 | /// |
| 839 | /// The kind of `Drop` impl generated depends on `pinned_drop` field: |
| 840 | /// - `Some` - implements `Drop` via `PinnedDrop` impl. |
| 841 | /// - `None` - generates code that ensures that `Drop` trait is not implemented, |
| 842 | /// instead of generating `Drop` impl. |
| 843 | fn make_drop_impl(cx: &Context<'_>) -> TokenStream { |
| 844 | let ident = cx.orig.ident; |
| 845 | let (impl_generics, ty_generics, where_clause) = cx.orig.generics.split_for_impl(); |
| 846 | |
| 847 | if let Some(span) = cx.pinned_drop { |
| 848 | // For interoperability with `forbid(unsafe_code)`, `unsafe` token should be |
| 849 | // call-site span. |
| 850 | let unsafety = <Token![unsafe]>::default(); |
| 851 | quote_spanned! { span => |
| 852 | impl #impl_generics _pin_project::__private::Drop for #ident #ty_generics |
| 853 | #where_clause |
| 854 | { |
| 855 | fn drop(&mut self) { |
| 856 | #unsafety { |
| 857 | // Safety - we're in 'drop', so we know that 'self' will |
| 858 | // never move again. |
| 859 | let __pinned_self = _pin_project::__private::Pin::new_unchecked(self); |
| 860 | // We call `pinned_drop` only once. Since `PinnedDrop::drop` |
| 861 | // is an unsafe method and a private API, it is never called again in safe |
| 862 | // code *unless the user uses a maliciously crafted macro*. |
| 863 | _pin_project::__private::PinnedDrop::drop(__pinned_self); |
| 864 | } |
| 865 | } |
| 866 | } |
| 867 | } |
| 868 | } else { |
| 869 | // If the user does not provide a `PinnedDrop` impl, |
| 870 | // we need to ensure that they don't provide a `Drop` impl of their |
| 871 | // own. |
| 872 | // Based on https://github.com/upsuper/assert-impl/blob/f503255b292ab0ba8d085b657f4065403cfa46eb/src/lib.rs#L80-L87 |
| 873 | // |
| 874 | // We create a new identifier for each struct, so that the traits |
| 875 | // for different types do not conflict with each other. |
| 876 | // |
| 877 | // Another approach would be to provide an empty Drop impl, |
| 878 | // which would conflict with a user-provided Drop impl. |
| 879 | // However, this would trigger the compiler's special handling |
| 880 | // of Drop types (e.g. fields cannot be moved out of a Drop type). |
| 881 | // This approach prevents the creation of needless Drop impls, |
| 882 | // giving users more flexibility. |
| 883 | let trait_ident = format_ident!("{}MustNotImplDrop" , ident); |
| 884 | |
| 885 | quote! { |
| 886 | // There are two possible cases: |
| 887 | // 1. The user type does not implement Drop. In this case, |
| 888 | // the first blanked impl will not apply to it. This code |
| 889 | // will compile, as there is only one impl of MustNotImplDrop for the user type |
| 890 | // 2. The user type does impl Drop. This will make the blanket impl applicable, |
| 891 | // which will then conflict with the explicit MustNotImplDrop impl below. |
| 892 | // This will result in a compilation error, which is exactly what we want. |
| 893 | trait #trait_ident {} |
| 894 | #[allow(clippy::drop_bounds, drop_bounds)] |
| 895 | impl<T: _pin_project::__private::Drop> #trait_ident for T {} |
| 896 | impl #impl_generics #trait_ident for #ident #ty_generics #where_clause {} |
| 897 | |
| 898 | // Generate a dummy impl of `PinnedDrop`, to ensure that the user cannot implement it. |
| 899 | // Since the user did not pass `PinnedDrop` to `#[pin_project]`, any `PinnedDrop` |
| 900 | // impl will not actually be called. Unfortunately, we can't detect this situation |
| 901 | // directly from either the `#[pin_project]` or `#[pinned_drop]` attributes, since |
| 902 | // we don't know what other attributes/impl may exist. |
| 903 | // |
| 904 | // To ensure that users don't accidentally write a non-functional `PinnedDrop` |
| 905 | // impls, we emit one ourselves. If the user ends up writing a `PinnedDrop` impl, |
| 906 | // they'll get a "conflicting implementations of trait" error when coherence |
| 907 | // checks are run. |
| 908 | #[doc(hidden)] |
| 909 | impl #impl_generics _pin_project::__private::PinnedDrop for #ident #ty_generics |
| 910 | #where_clause |
| 911 | { |
| 912 | unsafe fn drop(self: _pin_project::__private::Pin<&mut Self>) {} |
| 913 | } |
| 914 | } |
| 915 | } |
| 916 | } |
| 917 | |
| 918 | /// Creates an implementation of the projection methods. |
| 919 | /// |
| 920 | /// On structs, both the `project` and `project_ref` methods are always generated, |
| 921 | /// and the `project_replace` method is only generated if `ProjReplace::span` is `Some`. |
| 922 | /// |
| 923 | /// On enums, only methods that the returned projected type is named will be generated. |
| 924 | fn make_proj_impl( |
| 925 | cx: &Context<'_>, |
| 926 | proj_body: &TokenStream, |
| 927 | proj_ref_body: &TokenStream, |
| 928 | proj_own_body: &TokenStream, |
| 929 | ) -> TokenStream { |
| 930 | let vis = &cx.proj.vis; |
| 931 | let lifetime = &cx.proj.lifetime; |
| 932 | let orig_ident = cx.orig.ident; |
| 933 | let proj_ident = &cx.proj.mut_ident; |
| 934 | let proj_ref_ident = &cx.proj.ref_ident; |
| 935 | let proj_own_ident = &cx.proj.own_ident; |
| 936 | |
| 937 | let orig_ty_generics = cx.orig.generics.split_for_impl().1; |
| 938 | let proj_ty_generics = cx.proj.generics.split_for_impl().1; |
| 939 | let (impl_generics, ty_generics, where_clause) = cx.orig.generics.split_for_impl(); |
| 940 | // TODO: For enums and project_replace, dead_code warnings should not be |
| 941 | // allowed because methods are not generated unless explicitly specified. |
| 942 | // However, there is currently no good way to allow warnings for generated |
| 943 | // code, so we allow warnings for all methods for now. |
| 944 | let allow_dead_code = quote! { #[allow(dead_code)] }; |
| 945 | |
| 946 | let mut project = Some(quote! { |
| 947 | #allow_dead_code |
| 948 | #[inline] |
| 949 | #vis fn project<#lifetime>( |
| 950 | self: _pin_project::__private::Pin<lifetime mut Self>, |
| 951 | ) -> #proj_ident #proj_ty_generics { |
| 952 | unsafe { |
| 953 | #proj_body |
| 954 | } |
| 955 | } |
| 956 | }); |
| 957 | let mut project_ref = Some(quote! { |
| 958 | #allow_dead_code |
| 959 | #[allow(clippy::missing_const_for_fn)] |
| 960 | #[inline] |
| 961 | #vis fn project_ref<#lifetime>( |
| 962 | self: _pin_project::__private::Pin<lifetime Self>, |
| 963 | ) -> #proj_ref_ident #proj_ty_generics { |
| 964 | unsafe { |
| 965 | #proj_ref_body |
| 966 | } |
| 967 | } |
| 968 | }); |
| 969 | let mut project_replace = cx.project_replace.span().map(|span| { |
| 970 | // It is enough to only set the span of the signature. |
| 971 | let sig = quote_spanned! { span => |
| 972 | #allow_dead_code |
| 973 | #[inline] |
| 974 | #vis fn project_replace( |
| 975 | self: _pin_project::__private::Pin<&mut Self>, |
| 976 | __replacement: Self, |
| 977 | ) -> #proj_own_ident #orig_ty_generics |
| 978 | }; |
| 979 | quote! { |
| 980 | #sig { |
| 981 | unsafe { |
| 982 | let __self_ptr: *mut Self = self.get_unchecked_mut(); |
| 983 | |
| 984 | // Destructors will run in reverse order, so next create a guard to overwrite |
| 985 | // `self` with the replacement value without calling destructors. |
| 986 | let __guard = _pin_project::__private::UnsafeOverwriteGuard::new( |
| 987 | __self_ptr, |
| 988 | __replacement, |
| 989 | ); |
| 990 | |
| 991 | #proj_own_body |
| 992 | } |
| 993 | } |
| 994 | } |
| 995 | }); |
| 996 | |
| 997 | if cx.kind == Enum { |
| 998 | if !cx.project { |
| 999 | project = None; |
| 1000 | } |
| 1001 | if !cx.project_ref { |
| 1002 | project_ref = None; |
| 1003 | } |
| 1004 | if cx.project_replace.ident().is_none() { |
| 1005 | project_replace = None; |
| 1006 | } |
| 1007 | } |
| 1008 | |
| 1009 | quote! { |
| 1010 | impl #impl_generics #orig_ident #ty_generics #where_clause { |
| 1011 | #project |
| 1012 | #project_ref |
| 1013 | #project_replace |
| 1014 | } |
| 1015 | } |
| 1016 | } |
| 1017 | |
| 1018 | /// Checks that the `[repr(packed)]` attribute is not included. |
| 1019 | /// |
| 1020 | /// This currently does two checks: |
| 1021 | /// - Checks the attributes of structs to ensure there is no `[repr(packed)]`. |
| 1022 | /// - Generates a function that borrows fields without an unsafe block and |
| 1023 | /// forbidding `unaligned_references` lint. |
| 1024 | fn ensure_not_packed(orig: &OriginalType<'_>, fields: Option<&Fields>) -> Result<TokenStream> { |
| 1025 | for attr in orig.attrs { |
| 1026 | if let Meta::List(ref list) = attr.meta { |
| 1027 | if list.path.is_ident("repr" ) { |
| 1028 | for repr in list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)? { |
| 1029 | if repr.path().is_ident("packed" ) { |
| 1030 | let msg = if fields.is_none() { |
| 1031 | // #[repr(packed)] cannot be apply on enums and will be rejected by rustc. |
| 1032 | // However, we should not rely on the behavior of rustc that rejects this. |
| 1033 | // https://github.com/taiki-e/pin-project/pull/324#discussion_r612388001 |
| 1034 | "#[repr(packed)] attribute should be applied to a struct or union" |
| 1035 | } else if repr.require_name_value().is_ok() { |
| 1036 | // #[repr(packed = "")] is not valid format of #[repr(packed)] and will be |
| 1037 | // rejected by rustc. |
| 1038 | // However, we should not rely on the behavior of rustc that rejects this. |
| 1039 | // https://github.com/taiki-e/pin-project/pull/324#discussion_r612388001 |
| 1040 | "#[repr(packed)] attribute should not be name-value pair" |
| 1041 | } else { |
| 1042 | "#[pin_project] attribute may not be used on #[repr(packed)] types" |
| 1043 | }; |
| 1044 | bail!(repr, msg); |
| 1045 | } |
| 1046 | } |
| 1047 | } |
| 1048 | } |
| 1049 | } |
| 1050 | |
| 1051 | let fields = match fields { |
| 1052 | Some(fields) => fields, |
| 1053 | None => return Ok(TokenStream::new()), |
| 1054 | }; |
| 1055 | |
| 1056 | // Workaround for https://github.com/taiki-e/pin-project/issues/32 |
| 1057 | // Through the tricky use of proc macros, it's possible to bypass |
| 1058 | // the above check for the `repr` attribute. |
| 1059 | // To ensure that it's impossible to use pin projections on a `#[repr(packed)]` |
| 1060 | // struct, we generate code like this: |
| 1061 | // |
| 1062 | // ```rust |
| 1063 | // #[forbid(unaligned_references)] |
| 1064 | // fn assert_not_repr_packed(val: &MyStruct) { |
| 1065 | // let _field_1 = &val.field_1; |
| 1066 | // let _field_2 = &val.field_2; |
| 1067 | // ... |
| 1068 | // let _field_n = &val.field_n; |
| 1069 | // } |
| 1070 | // ``` |
| 1071 | // |
| 1072 | // Taking a reference to a packed field is UB, and applying |
| 1073 | // `#[forbid(unaligned_references)]` makes sure that doing this is a hard error. |
| 1074 | // |
| 1075 | // If the struct ends up having `#[repr(packed)]` applied somehow, |
| 1076 | // this will generate an (unfriendly) error message. Under all reasonable |
| 1077 | // circumstances, we'll detect the `#[repr(packed)]` attribute, and generate |
| 1078 | // a much nicer error above. |
| 1079 | // |
| 1080 | // There is one exception: If the type of a struct field has an alignment of 1 |
| 1081 | // (e.g. u8), it is always safe to take a reference to it, even if the struct |
| 1082 | // is `#[repr(packed)]`. If the struct is composed entirely of types of |
| 1083 | // alignment 1, our generated method will not trigger an error if the |
| 1084 | // struct is `#[repr(packed)]`. |
| 1085 | // |
| 1086 | // Fortunately, this should have no observable consequence - `#[repr(packed)]` |
| 1087 | // is essentially a no-op on such a type. Nevertheless, we include a test |
| 1088 | // to ensure that the compiler doesn't ever try to copy the fields on |
| 1089 | // such a struct when trying to drop it - which is reason we prevent |
| 1090 | // `#[repr(packed)]` in the first place. |
| 1091 | // |
| 1092 | // See also https://github.com/taiki-e/pin-project/pull/34. |
| 1093 | // |
| 1094 | // Note: |
| 1095 | // - Lint-based tricks aren't perfect, but they're much better than nothing: |
| 1096 | // https://github.com/taiki-e/pin-project-lite/issues/26 |
| 1097 | // |
| 1098 | // - Enable both unaligned_references and safe_packed_borrows lints |
| 1099 | // because unaligned_references lint does not exist in older compilers: |
| 1100 | // https://github.com/taiki-e/pin-project-lite/pull/55 |
| 1101 | // https://github.com/rust-lang/rust/pull/82525 |
| 1102 | let mut field_refs = vec![]; |
| 1103 | match fields { |
| 1104 | Fields::Named(FieldsNamed { named, .. }) => { |
| 1105 | for Field { ident, .. } in named { |
| 1106 | field_refs.push(quote!(&this.#ident)); |
| 1107 | } |
| 1108 | } |
| 1109 | Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => { |
| 1110 | for (index, _) in unnamed.iter().enumerate() { |
| 1111 | let index = Index::from(index); |
| 1112 | field_refs.push(quote!(&this.#index)); |
| 1113 | } |
| 1114 | } |
| 1115 | Fields::Unit => {} |
| 1116 | } |
| 1117 | |
| 1118 | let (impl_generics, ty_generics, where_clause) = orig.generics.split_for_impl(); |
| 1119 | let ident = orig.ident; |
| 1120 | Ok(quote! { |
| 1121 | #[forbid(unaligned_references, safe_packed_borrows)] |
| 1122 | fn __assert_not_repr_packed #impl_generics (this: ident #ty_generics) #where_clause { |
| 1123 | #(let _ = #field_refs;)* |
| 1124 | } |
| 1125 | }) |
| 1126 | } |
| 1127 | |