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