diff --git a/examples/selfref.rs b/examples/selfref.rs new file mode 100644 index 00000000..5c6e768a --- /dev/null +++ b/examples/selfref.rs @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + +use pin_init::*; + +#[pin_data] +struct SelfRef { + #[not_covariant] + not_cov: Box bool + 'str>, + + part: &'str str, + str: String, + + #[borrows(mut mut_str)] + mut_part: &'mut_str mut str, + mut_str: String, +} + +fn use_self_ref() { + stack_pin_init!(let foo = pin_init!(SelfRef { + str: "hello world".to_owned(), + part: &str[..5], + mut_str: "hello world".to_owned(), + mut_part: &mut mut_str[..5], + not_cov: Box::new(move |s| s == str), + })); + + // Access via projection. + println!("{}", foo.as_mut().project().part); + + // Access via accessor. + println!("{}", foo.part()); + + // Access via `with_project`, gives mutable reference. + foo.as_mut().with_project(|proj| { + *proj.part = &proj.str[5..]; + }); + + println!("{}", foo.part()); + + // Access fields that mutable borrow others are similar to those of shared borrow. + println!("{}", foo.as_mut().project().mut_part); + println!("{}", foo.mut_part()); + foo.as_mut().with_project(|proj| { + proj.mut_part.make_ascii_uppercase(); + }); + + // Access non-covariant type using `with_` accessor. + foo.with_not_cov(|not_cov| { + not_cov(""); + }); + + // Access non-covariant type using `with_project`. + foo.as_mut().with_project(|proj| (proj.not_cov)(proj.str)); +} + +fn main() { + use_self_ref(); +} diff --git a/internal/Cargo.toml b/internal/Cargo.toml index 4b77202d..eed20769 100644 --- a/internal/Cargo.toml +++ b/internal/Cargo.toml @@ -16,7 +16,7 @@ proc-macro = true [dependencies] quote = "1.0" proc-macro2 = "1.0" -syn = { version = "2.0.86", features = ["full", "parsing", "visit-mut"] } +syn = { version = "2.0.86", features = ["full", "parsing", "visit", "visit-mut"] } [build-dependencies] rustc_version = "0.4" diff --git a/internal/src/init.rs b/internal/src/init.rs index 28d30805..09396537 100644 --- a/internal/src/init.rs +++ b/internal/src/init.rs @@ -145,6 +145,7 @@ pub(crate) fn expand( let data = Ident::new("__data", Span::mixed_site()); let init_fields = init_fields(&fields, pinned, &data, &slot); let field_check = make_field_check(&fields, init_kind, &path); + Ok(quote! {{ // Get the data about fields from the supplied type. // SAFETY: TODO @@ -156,7 +157,7 @@ pub(crate) fn expand( }; // Ensure that `#data` really is of type `#data` and help with type inference: let init = #data.__make_closure::<_, #error>( - move |slot| { + move |slot, #data| { #zeroable_check #this #init_fields @@ -166,7 +167,7 @@ pub(crate) fn expand( } ); let init = move |slot| -> ::core::result::Result<(), #error> { - init(slot).map(|__InitOk| ()) + init(slot, #data.__with_lt()).map(|__InitOk| ()) }; // SAFETY: TODO unsafe { ::pin_init::#init_from_closure::<_, #error>(init) } diff --git a/internal/src/lib.rs b/internal/src/lib.rs index 60d5093f..19470975 100644 --- a/internal/src/lib.rs +++ b/internal/src/lib.rs @@ -8,6 +8,7 @@ // Documentation is done in the pin-init crate instead. #![allow(missing_docs)] +#![cfg_attr(USE_RUSTC_FEATURES, feature(extract_if))] use proc_macro::TokenStream; use syn::parse_macro_input; @@ -18,6 +19,7 @@ mod diagnostics; mod init; mod pin_data; mod pinned_drop; +mod util; mod zeroable; #[proc_macro_attribute] diff --git a/internal/src/pin_data.rs b/internal/src/pin_data.rs index 9fbbd25b..b20256bb 100644 --- a/internal/src/pin_data.rs +++ b/internal/src/pin_data.rs @@ -1,16 +1,23 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT -use proc_macro2::TokenStream; -use quote::{format_ident, quote}; +use std::collections::{BTreeMap, BTreeSet}; + +use proc_macro2::{Span, TokenStream}; +use quote::{format_ident, quote, quote_spanned, ToTokens}; use syn::{ parse::{End, Nothing, Parse}, parse_quote, parse_quote_spanned, + punctuated::Punctuated, spanned::Spanned, visit_mut::VisitMut, - Attribute, Field, Generics, Ident, Item, PathSegment, Type, TypePath, Visibility, WhereClause, + Attribute, Field, Fields, GenericParam, Generics, Ident, Item, ItemStruct, Lifetime, + LifetimeParam, PathSegment, Token, Type, TypePath, Visibility, WhereClause, }; -use crate::diagnostics::{DiagCtxt, ErrorGuaranteed}; +use crate::{ + diagnostics::{DiagCtxt, ErrorGuaranteed}, + util::{Binder, GenericsExt, LifetimeExt, TypeExt}, +}; pub(crate) mod kw { syn::custom_keyword!(PinnedDrop); @@ -35,9 +42,69 @@ impl Parse for Args { } } +/// Annotation that a field is referenced. +struct Borrow { + mutable: Option, + /// Name of the field that this lifetime can reference. + field: Ident, +} + +impl Parse for Borrow { + fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result { + Ok(Borrow { + mutable: input.parse()?, + field: input.parse()?, + }) + } +} + +#[expect(unused)] +enum Variance { + Covariant(Span), + NotCovariant(Span), +} + +impl Variance { + fn parse(dcx: &mut DiagCtxt, attrs: &mut Vec, span: Span) -> Self { + let mut attrs = attrs.extract_if(.., |attr| { + attr.path().is_ident("covariant") || attr.path().is_ident("not_covariant") + }); + + let result = match attrs.next() { + None => { + // By default, infer covariance. + Variance::Covariant(span) + } + Some(attr) => { + if attr.path().is_ident("covariant") { + Variance::Covariant(attr.path().span()) + } else { + Variance::NotCovariant(attr.path().span()) + } + } + }; + + // Emit error on redundant specifications. + for attr in attrs { + dcx.error(attr.path(), "variance marker can only be specified once"); + } + + result + } +} + +#[derive(PartialEq, Eq, Clone, Copy)] +enum Borrowed { + Shared, + Mutable, +} + struct FieldInfo<'a> { field: &'a Field, pinned: bool, + borrowed: Option, + variance: Option, + ty: Binder, cfg_attrs: Vec<&'a Attribute>, } @@ -79,14 +146,86 @@ pub(crate) fn pin_data( replacer.visit_generics_mut(&mut struct_.generics); replacer.visit_fields_mut(&mut struct_.fields); - let fields: Vec> = struct_ - .fields + // Move `fields` out from the struct. + let mut fields = struct_.fields; + struct_.fields = Fields::Unit; + + // Collect all bound lifetimes from generics. + let bound_lifetimes: BTreeSet<&Lifetime> = + struct_.generics.lifetimes().map(|x| &x.lifetime).collect(); + + // Keep track on how fields are borrowed and where. + let mut borrow_info = BTreeMap::<_, (bool, Vec)>::new(); + + let mut fields: Vec> = fields .iter_mut() .map(|field| { let len = field.attrs.len(); field.attrs.retain(|a| !a.path().is_ident("pin")); let pinned = len != field.attrs.len(); + let ident = field.ident.as_ref().unwrap(); + + // Parse `#[covariant]` and `#[not_covariant]` markers. + let variance = Variance::parse(dcx, &mut field.attrs, ident.span()); + + // Parse `#[borrows]` attribute or infer it from the type. + let borrows: Punctuated = field + .attrs + .extract_if(.., |attr| attr.path().is_ident("borrows")) + .filter_map( + |attr| match attr.parse_args_with(Punctuated::parse_terminated) { + Ok(v) => Some(v), + Err(err) => { + dcx.error(attr, err); + None + } + }, + ) + .next() + .unwrap_or_else(|| { + // If no annotations are attached, infer lifetime based on the field referenced, + // although bound lifetimes from struct generics should take priority. + // + // For example, + // ``` + // struct Foo<'a> { + // bar: &'a (), + // a: u32, + // } + // ``` + // would not be inferred as self-referential because `'a` is already bound by the + // struct generics. + // + // Note that we cannot reliably determine what type of borrow is needed on fields. + // More commonly a shared borrow is required, so it is inferred as such. Mutable + // borrow would require explicit user annotation. + field + .ty + .unbound_lifetimes() + .difference(&bound_lifetimes) + .map(|<| Borrow { + mutable: None, + field: lt.ident.clone(), + }) + .collect() + }); + + // Update borrow information. + for borrow in borrows.iter() { + let entry = borrow_info.entry(borrow.field.clone()).or_default(); + entry.0 |= borrow.mutable.is_some(); + entry.1.push(borrow.field.span()); + } + + let ty = Binder::new( + borrows + .iter() + .map(|field_ref| Lifetime::from_ident(&field_ref.field)) + .collect(), + field.ty.clone(), + ); + let cfg_attrs = field .attrs .iter() @@ -96,38 +235,114 @@ pub(crate) fn pin_data( FieldInfo { field: &*field, pinned, + borrowed: None, + variance: if borrows.is_empty() { + None + } else { + Some(variance) + }, + ty, cfg_attrs, } }) .collect(); - for field in &fields { + for field in fields.iter_mut() { let ident = field.field.ident.as_ref().unwrap(); if !field.pinned && is_phantom_pinned(&field.field.ty) { dcx.warn( field.field, format!( - "The field `{ident}` of type `PhantomPinned` only has an effect \ + "The field `{}` of type `PhantomPinned` only has an effect \ if it has the `#[pin]` attribute", + ident, ), ); } + + // Update `borrowed` now we have all borrow information recorded. + // This is not `remove()` because fields might be `#[cfg]` on so identifier names are not unique. + if let Some((mutable, users)) = borrow_info.get_mut(ident) { + field.borrowed = Some(if *mutable { + Borrowed::Mutable + } else { + Borrowed::Shared + }); + + users.clear(); + } + + // We have a limitation of up to 4 referenced lifetime in a type. + // See `ForLt4` and `SelfRef` in `__internal.rs` on why this limitation exists. + if field.ty.bound.len() > 4 { + dcx.error( + &field.ty.bound[4], + "at most 4 lifetimes can be referenced from a struct at a time", + ); + } + } + + // For any residual users in `borrow_info`, the lifetime mention is invalid and report as such. + for (name, (_, users)) in borrow_info { + for user in users { + dcx.error( + user, + format!("`{name}` is neither a lifetime in generics nor a field name"), + ); + } } + // Create a lifetime parameter for each field. + let field_lts: Vec<_> = fields + .iter() + .filter(|f| f.borrowed.is_some()) + .map(|f| Lifetime::from_ident(f.field.ident.as_ref().unwrap())) + .collect(); + let field_lt_generics = Generics { + lt_token: Some(Default::default()), + params: field_lts + .iter() + .zip(std::iter::once(None).chain(field_lts.iter().map(Some))) + .map(|(lt, prev)| { + GenericParam::Lifetime(LifetimeParam { + attrs: Vec::new(), + lifetime: lt.clone(), + colon_token: prev.map(|_| Default::default()), + bounds: prev.iter().map(|<| lt.clone()).collect(), + }) + }) + .collect(), + gt_token: Some(Default::default()), + where_clause: None, + }; + + let struct_def = generate_struct_def(&struct_, &fields); let unpin_impl = generate_unpin_impl(&struct_.ident, &struct_.generics, &fields); let drop_impl = generate_drop_impl(&struct_.ident, &struct_.generics, args); - let projections = - generate_projections(&struct_.vis, &struct_.ident, &struct_.generics, &fields); - let the_pin_data = - generate_the_pin_data(&struct_.vis, &struct_.ident, &struct_.generics, &fields); + let drop_check = generate_drop_check(dcx, &struct_, &fields, &field_lt_generics); + let projections = generate_projections( + &struct_.vis, + &struct_.ident, + &struct_.generics, + &fields, + &field_lt_generics, + ); + let the_pin_data = generate_the_pin_data( + &struct_.vis, + &struct_.ident, + &struct_.generics, + &fields, + &field_lt_generics, + ); Ok(quote! { - #struct_ - #projections + #struct_def // We put the rest into this const item, because it then will not be accessible to anything // outside. const _: () = { + #drop_check + #projections #the_pin_data #unpin_impl #drop_impl @@ -159,6 +374,67 @@ fn is_phantom_pinned(ty: &Type) -> bool { } } +fn generate_struct_def( + ItemStruct { + attrs, + vis, + struct_token, + ident, + generics, + fields: _, + semi_token, + }: &ItemStruct, + fields: &[FieldInfo<'_>], +) -> TokenStream { + let mut generated_fields = Vec::new(); + + for field in fields { + let Field { + attrs, + vis, + mutability: _, + ident, + colon_token, + ty: _, + } = &field.field; + + let mut ty = field.ty.value.to_token_stream(); + + if field.variance.is_some() { + let bound = field.ty.for_bound_4(); + let bound_lt = bound.lifetimes.iter(); + ty = parse_quote!(::pin_init::__internal::SelfRef< + ::pin_init::__internal::ForLtImpl< + dyn #bound ::pin_init::__internal::WithLt4<#(#bound_lt,)* Of = #ty> + > + >); + }; + + // For mutably referenced items, it is possible to access `&struct.field` through `Pin<&mut + // Struct>`, which conflicts with the mutable access possible via the mutable borrow when + // constructing. Wrap the type behind `UnsafePinned` so it's not UB to have both and it also + // blocks user from doing anything wiht the value (safely). + if field.borrowed == Some(Borrowed::Mutable) { + ty = parse_quote!(::pin_init::__internal::UnsafePinned<#ty>); + } + + generated_fields.push(quote! { + #(#attrs)* #vis #ident #colon_token #ty + }); + } + + let whr = &generics.where_clause; + + quote!( + #(#attrs)* + #vis + #struct_token #ident #generics #whr { + #(#generated_fields,)* + } + #semi_token + ) +} + fn generate_unpin_impl( ident: &Ident, generics: &Generics, @@ -179,15 +455,25 @@ fn generate_unpin_impl( else { unreachable!() }; - let pinned_fields = fields.iter().filter(|f| f.pinned).map(|f| { - let ident = f.field.ident.as_ref().unwrap(); - let ty = &f.field.ty; - let cfg_attrs = &f.cfg_attrs; + + let pinned_fields = if fields.iter().any(|f| f.borrowed.is_some()) { + // Self-referential structs must always be pinned. quote!( - #(#cfg_attrs)* - #ident: #ty + __phantom_pinned: ::core::marker::PhantomPinned, ) - }); + } else { + let pinned_fields = fields.iter().filter(|f| f.pinned).map(|f| { + let ident = f.field.ident.as_ref().unwrap(); + let ty = &f.field.ty; + let cfg_attrs = &f.cfg_attrs; + quote!( + #(#cfg_attrs)* + #ident: #ty + ) + }); + quote!(#(#pinned_fields,)*) + }; + quote! { // This struct will be used for the unpin analysis. It is needed, because only structurally // pinned fields are relevant whether the struct should implement `Unpin`. @@ -201,7 +487,7 @@ fn generate_unpin_impl( { __phantom_pin: ::pin_init::__internal::PhantomInvariantLifetime<'__pin>, __phantom: ::pin_init::__internal::PhantomInvariant<#ident #ty_generics>, - #(#pinned_fields),* + #pinned_fields } #[doc(hidden)] @@ -262,74 +548,450 @@ fn generate_drop_impl(ident: &Ident, generics: &Generics, args: Args) -> TokenSt } } +fn generate_drop_check( + dcx: &mut DiagCtxt, + struct_: &ItemStruct, + fields: &[FieldInfo<'_>], + field_lt_generics: &Generics, +) -> TokenStream { + let struct_name = &struct_.ident; + // If the struct is not self-referential then we can just skip. However, still leave a + // `__DropCheck` type around which can be used to capture all field lifetimes by other + // generated code. + if fields.iter().all(|f| f.borrowed.is_none()) { + return quote!( + use #struct_name as __DropCheck; + ); + } + + // Make sure fields are dropped earlier than the fields that they borrow. + // + // Note that this only order fields and their borrow, and not establish the order between two + // borrows. The latter is checked below with `__drop_check`. We could also use `__drop_check` + // to perform what we check here, but it'll require synthesize lifetimes for more fields and + // will emit a less clear error message. + for (i, field) in fields.iter().enumerate() { + let ident = field.field.ident.as_ref().unwrap(); + for lt in &field.ty.bound { + let borrowed_field = <.ident; + + fields + .iter() + .enumerate() + .take(i) + .filter(|f| f.1.field.ident.as_ref().unwrap() == borrowed_field) + .for_each(|_| { + dcx.error( + borrowed_field, + format!("field `{ident}` borrows `{borrowed_field}`, but drops later",), + ); + }); + } + } + + let mut generics_with_field_lt = struct_.generics.clone(); + generics_with_field_lt + .params + .extend(field_lt_generics.params.iter().cloned()); + + let field_lt_for_generics = field_lt_generics.for_generics(); + let (_, ty_generics_with_field_lt, _) = generics_with_field_lt.split_for_impl(); + let (impl_generics, ty_generics, whr) = struct_.generics.split_for_impl(); + + // Wrap each field in a `PhantomInvariant`. For borrowed fields, additionally + // use `&#lt mut #ty` so the `lt` becomes associated with `#ty` which deduces + // implied bounds. + let phantom_fields = fields.iter().map(|f| { + let ty = &f.field.ty; + let cfg_attrs = &f.cfg_attrs; + let ident = f.field.ident.as_ref().unwrap(); + + if f.borrowed.is_some() { + let lt = Lifetime::from_ident(ident); + quote!( + #(#cfg_attrs)* + #ident: ::pin_init::__internal::PhantomInvariant<&#lt mut #ty>, + ) + } else { + quote!( + #(#cfg_attrs)* + #[allow(non_snake_case)] + #ident: ::pin_init::__internal::PhantomInvariant<#ty>, + ) + } + }); + + let guards = fields.iter().rev().map(|f| { + let ident = f.field.ident.as_ref().unwrap(); + let cfg_attrs = &f.cfg_attrs; + let span = ident.span(); + if f.borrowed.is_some() { + quote_spanned!(span => + // `LifetimeGuard` implements `Drop` and borrows `#ident`, so Rust must ensure that + // when the drop impl is called, `#ident` is still alive. Because the guards are + // generated in reverse field order, this ensures that the lifetimes of fields + // declared later must strictly outlive the lifetimes of fields declared earlier. + // + // For example, in + // ``` + // struct Foo { + // #[borrows(a, b)] + // x: &'b &'a (), + // a: String, + // y: PrintOnDrop<&'b str>, + // b: String, + // } + // ``` + // it ensures that `b` will strictly outlive `a`. + // + // This is needed because implied bounds exist. Rust needs to ensure that types are + // well-formed; in the above example, `&'b &'a ()` is well-formed only if `a` + // outlive `b`. To avoid requiring everyone from having to express this bound + // explicitly when declaring a struct, the `'b: 'a` bound is inferred by the Rust + // compiler. However this causes an issue, where now `&'a str` can be coerced to + // `&'b str` because compiler thinks that it shorten the lifetime. This is + // catastrophical for self-referencing structs, in the above example we'll be able + // to put a reference to `a` into `y`; but `a` drops first, so when `y` drops, it + // accesses `a` and causes a use-after-free! + // + // The code here essentially reconstruct the dropping order and ask the compiler to + // check that this won't cause an issue. + #(#cfg_attrs)* + let #ident = ::pin_init::__internal::PhantomInvariant::new(); + // `LifetimeGuard::new` has signature of `(&'a PhantomData<&'a mut T>) -> + // LifetimeGuard<'a>`. So it serves two purpose: tie the lifetime of binding and the + // lifetime in the parameter together, and also borrows it. + #(#cfg_attrs)* + let _guard = ::pin_init::__internal::LifetimeGuard::new(&#ident); + ) + } else { + quote!( + // No lifetimes to tie for fields that are not borrowed. + #(#cfg_attrs)* + let #ident = ::pin_init::__internal::PhantomInvariant::new(); + ) + } + }); + + let fields = fields.iter().map(|f| { + let cfg_attrs = &f.cfg_attrs; + let ident = f.field.ident.as_ref().unwrap(); + quote!(#(#cfg_attrs)* #ident,) + }); + + let struct_span = struct_.ident.span().resolved_at(Span::mixed_site()); + quote_spanned! {struct_span => + #[doc(hidden)] + #[allow(non_snake_case)] + struct __DropCheck #generics_with_field_lt + #whr + { + #(#phantom_fields)* + } + + #[allow(non_snake_case)] + fn __drop_check #impl_generics ( + // This must be present so the function can observe the implied bounds. + _: &#struct_name #ty_generics, + f: impl #field_lt_for_generics ::core::ops::FnOnce(__DropCheck #ty_generics_with_field_lt) + ) #whr { + #(#guards)* + + f(__DropCheck { + #(#fields)* + }) + } + } +} + fn generate_projections( vis: &Visibility, ident: &Ident, generics: &Generics, fields: &[FieldInfo<'_>], + field_lt_generics: &Generics, ) -> TokenStream { + let pin_lt = Lifetime::new("'__pin", Span::mixed_site()); + let (impl_generics, ty_generics, _) = generics.split_for_impl(); let mut generics_with_pin_lt = generics.clone(); - generics_with_pin_lt.params.insert(0, parse_quote!('__pin)); + generics_with_pin_lt.params.insert( + 0, + GenericParam::Lifetime(LifetimeParam::new(pin_lt.clone())), + ); let (_, ty_generics_with_pin_lt, whr) = generics_with_pin_lt.split_for_impl(); - let projection = format_ident!("{ident}Projection"); + + let mut generics_with_field_lt = generics.clone(); + generics_with_field_lt + .params + .extend(field_lt_generics.params.iter().cloned()); + let field_lt_for_generics = field_lt_generics.for_generics(); + let (_, ty_generics_with_field_lt, _) = generics_with_field_lt.split_for_impl(); + + let mut generics_with_pin_field_lt = generics_with_field_lt.clone(); + generics_with_pin_field_lt.params.insert( + 0, + GenericParam::Lifetime(LifetimeParam::new(pin_lt.clone())), + ); + let (_, ty_generics_with_pin_field_lt, _) = generics_with_pin_field_lt.split_for_impl(); + let this = format_ident!("this"); let (fields_decl, fields_proj): (Vec<_>, Vec<_>) = fields .iter() - .map(|field| { - let Field { vis, ident, ty, .. } = &field.field; - let cfg_attrs = &field.cfg_attrs; + .filter(|f| { + // Mutably referenced fields cannot be accessed by user at all for aliasing reasons. + if f.borrowed == Some(Borrowed::Mutable) { + return false; + } + + // If the type is not covariant, it must omitted from non-with projection, as such + // projection shortens the lifetime from fields to '__pin. + if matches!(f.variance, Some(Variance::NotCovariant(_))) { + return false; + } + + true + }) + .map(|f| { + let vis = &f.field.vis; + let cfg_attrs = &f.cfg_attrs; + let ident = f + .field + .ident + .as_ref() + .expect("only structs with named fields are supported"); + + // if `f.ty` contains field lifetimes, which we need to replace them with shorter + // `'__pin` lifetime as field lifetimes are not available in this context. + let ty = f.ty.instantiate(&pin_lt); + + // Fields shared-referenced by other fields can only be shared accessed. Fields that + // references other field and are covariant can also only be given shared reference + // as mutable reference is invariant. + let mut_token: Option = if f.borrowed.is_none() && f.variance.is_none() { + Some(Default::default()) + } else { + None + }; + + let mut accessor = quote!(&#mut_token #this.#ident); + if f.variance.is_some() { + accessor = quote!( + // SAFETY: we have `SelfRef<..>` which we know is layout compatible with `f.ty`. + // Field lifetimes in `f.ty` can be shortened to `#ty` due to covariance (which + // is checked later). + unsafe { core::mem::transmute::<_, &#mut_token #ty>(#accessor) } + ) + } + + if f.pinned { + ( + quote!( + #(#cfg_attrs)* + #vis #ident: ::core::pin::Pin<&'__pin #mut_token #ty>, + ), + quote!( + #(#cfg_attrs)* + // SAFETY: this field is structurally pinned. + #ident: unsafe { ::core::pin::Pin::new_unchecked(#accessor) }, + ), + ) + } else { + ( + quote!( + #(#cfg_attrs)* + #vis #ident: &'__pin #mut_token #ty, + ), + quote!( + #(#cfg_attrs)* + #ident: #accessor, + ), + ) + } + }) + .collect(); - let ident = ident + let (fields_decl_lt, fields_proj_lt): (Vec<_>, Vec<_>) = fields + .iter() + .filter(|f| { + // Mutably referenced fields cannot be accessed by user at all for aliasing reasons. + f.borrowed != Some(Borrowed::Mutable) + }) + .map(|f| { + let vis = &f.field.vis; + let cfg_attrs = &f.cfg_attrs; + let ident = f + .field + .ident .as_ref() .expect("only structs with named fields are supported"); - if field.pinned { + + let ty = &f.ty.value; + + // Fields shared-referenced by other fields can only be shared accessed. + let mut_token: Option = if f.borrowed.is_none() { + Some(Default::default()) + } else { + None + }; + + let mut accessor = quote!(&#mut_token #this.#ident); + if f.variance.is_some() { + accessor = quote!( + // SAFETY: we have `SelfRef<..>` which we know is layout compatible with `f.ty`. + // We cannot include explicit type name here as the field lifetimes are nameable + // in this context. + unsafe { core::mem::transmute(#accessor) } + ) + } + + // In `with_project`, borrowed fields have their field lifetime available, so use it + // instead of `'__pin`. + let lt = if f.borrowed.is_some() { + &Lifetime::from_ident(ident) + } else { + &pin_lt + }; + + if f.pinned { ( quote!( #(#cfg_attrs)* - #vis #ident: ::core::pin::Pin<&'__pin mut #ty>, + #vis #ident: ::core::pin::Pin<&#lt #mut_token #ty>, ), quote!( #(#cfg_attrs)* // SAFETY: this field is structurally pinned. - #ident: unsafe { ::core::pin::Pin::new_unchecked(&mut #this.#ident) }, + #ident: unsafe { ::core::pin::Pin::new_unchecked(#accessor) }, ), ) } else { ( quote!( #(#cfg_attrs)* - #vis #ident: &'__pin mut #ty, + #vis #ident: &#lt #mut_token #ty, ), quote!( #(#cfg_attrs)* - #ident: &mut #this.#ident, + #ident: #accessor, ), ) } }) .collect(); - let structurally_pinned_fields_docs = fields + + let structurally_pinned_fields_docs: Vec<_> = fields .iter() .filter(|f| f.pinned) - .map(|f| format!(" - `{}`", f.field.ident.as_ref().unwrap())); - let not_structurally_pinned_fields_docs = fields + .map(|f| format!(" - `{}`", f.field.ident.as_ref().unwrap())) + .collect(); + let not_structurally_pinned_fields_docs: Vec<_> = fields .iter() .filter(|f| !f.pinned) - .map(|f| format!(" - `{}`", f.field.ident.as_ref().unwrap())); + .map(|f| format!(" - `{}`", f.field.ident.as_ref().unwrap())) + .collect(); let docs = format!(" Pin-projections of [`{ident}`]"); + + // For fields that references other fields, field access syntax stops working as they're wrapped + // behind `SelfRef` because their actual lifetime is not on the struct. + // + // Generate an accessor method for them. + let mut accessors = Vec::new(); + for f in fields { + let ident = f.field.ident.as_ref().unwrap(); + match f.variance { + // They can be accessed normally, no accessor to be generated. + None => continue, + + Some(Variance::Covariant(_)) => { + let f_doc = format!("Access the `{ident}` field on a shared reference of `Self`."); + let vis = &f.field.vis; + + // Use the span of type for better error message. + let span = f.ty.value.span().resolved_at(Span::mixed_site()); + + let ty = f.ty.instantiate(&pin_lt); + + let long = Lifetime::new("'__long", span); + let long_ty = f.ty.instantiate(&long); + + let short = Lifetime::new("'__short", span); + let short_ty = f.ty.instantiate(&short); + + // Add `<'__long: '__short, 'short>` as additional generics. + let mut covariance_check_generics = generics.clone(); + covariance_check_generics + .params + .push(GenericParam::Lifetime(LifetimeParam { + attrs: Vec::new(), + lifetime: long, + colon_token: Some(Default::default()), + bounds: std::iter::once(short.clone()).collect(), + })); + covariance_check_generics + .params + .push(GenericParam::Lifetime(LifetimeParam::new(short))); + + accessors.push(quote_spanned!(span => + #[doc = #f_doc] + #[inline] + #vis fn #ident<#pin_lt>(&#pin_lt self) -> &#pin_lt #ty { + // Emit a check to ensure the type is *really* covariant for soundness. + fn covariance_check #covariance_check_generics (long: #long_ty) -> #short_ty { + long + } + + // SAFETY: `SelfRef` is layout compatible with `#ty` and we have checked + // that it is covariant. + unsafe { core::mem::transmute(&self.#ident) } + } + )) + } + + Some(Variance::NotCovariant(_)) => { + let f_doc = format!("Access the `{ident}` field on a shared reference of `Self`."); + let vis = &f.field.vis; + let with_ident = format_ident!("with_{ident}"); + + let bound = f.ty.for_bound(); + let ty = &f.ty.value; + + accessors.push(quote!( + #[doc = #f_doc] + #[inline] + #vis fn #with_ident<'__this, R>(&'__this self, f: impl #bound ::core::ops::FnOnce(&'__this #ty) -> R) -> R { + // SAFETY: `SelfRef` is layout compatible with `#ty`. + f(unsafe { core::mem::transmute(&self.#ident) }) + } + )) + } + } + } + quote! { #[doc = #docs] // Allow `non_snake_case` since the same warning will be emitted on // the struct definition. #[allow(dead_code, non_snake_case)] #[doc(hidden)] - #vis struct #projection #generics_with_pin_lt + #vis struct __Projection #generics_with_pin_lt #whr { #(#fields_decl)* - ___pin_phantom_data: ::core::marker::PhantomData<&'__pin mut ()>, + ___pin_phantom_data: ::core::marker::PhantomData<&'__pin #ident #ty_generics>, + } + + #[doc = #docs] + // Allow `non_snake_case` since the same warning will be emitted on + // the struct definition. + #[allow(dead_code, non_snake_case)] + #[doc(hidden)] + #vis struct __ProjectionLt #generics_with_pin_field_lt + #whr + { + #(#fields_decl_lt)* + ___pin_phantom_data: ::core::marker::PhantomData<&'__pin mut __DropCheck #ty_generics_with_field_lt>, } impl #impl_generics #ident #ty_generics @@ -345,14 +1007,36 @@ fn generate_projections( #[inline] #vis fn project<'__pin>( self: ::core::pin::Pin<&'__pin mut Self>, - ) -> #projection #ty_generics_with_pin_lt { + ) -> __Projection #ty_generics_with_pin_lt { // SAFETY: we only give access to `&mut` for fields not structurally pinned. let #this = unsafe { ::core::pin::Pin::get_unchecked_mut(self) }; - #projection { + __Projection { #(#fields_proj)* ___pin_phantom_data: ::core::marker::PhantomData, } } + + /// Pin-projects all fields of `Self` with proper lifetime. + /// + /// These fields are structurally pinned: + #(#[doc = #structurally_pinned_fields_docs])* + /// + /// These fields are **not** structurally pinned: + #(#[doc = #not_structurally_pinned_fields_docs])* + #[inline] + #vis fn with_project<'__pin, R>( + self: ::core::pin::Pin<&'__pin mut Self>, + f: impl #field_lt_for_generics ::core::ops::FnOnce(__ProjectionLt #ty_generics_with_pin_field_lt) -> R + ) -> R { + // SAFETY: we only give access to `&mut` for fields not structurally pinned. + let #this = unsafe { ::core::pin::Pin::get_unchecked_mut(self) }; + f(__ProjectionLt { + #(#fields_proj_lt)* + ___pin_phantom_data: ::core::marker::PhantomData, + }) + } + + #(#accessors)* } } } @@ -362,27 +1046,74 @@ fn generate_the_pin_data( struct_name: &Ident, generics: &Generics, fields: &[FieldInfo<'_>], + field_lt_generics: &Generics, ) -> TokenStream { + let mut generics_with_field_lt = generics.clone(); + generics_with_field_lt + .params + .extend(field_lt_generics.params.iter().cloned()); + + let field_lt_for_generics = field_lt_generics.for_generics(); + let (impl_generics_with_lt, ty_generics_with_field_lt, _) = + generics_with_field_lt.split_for_impl(); let (impl_generics, ty_generics, whr) = generics.split_for_impl(); - // For every field, we create an initializing projection function according to its projection - // type. If a field is structurally pinned, we create a `Slot` with `Pinned` which must be - // initialized via `PinInit`; if it is not structurally pinned, then we create a `Slot` with - // `Unpinned` which allows initialization via `Init`. let field_accessors = fields .iter() .map(|f| { - let Field { vis, ident, ty, .. } = f.field; + let vis = &f.field.vis; let cfg_attrs = &f.cfg_attrs; - - let field_name = ident + let field_name = f + .field + .ident .as_ref() .expect("only structs with named fields are supported"); + let ty = &f.field.ty; + + let lt = Lifetime::from_ident(field_name); + let pin_marker = if f.pinned { quote!(Pinned) } else { quote!(Unpinned) }; + + let (slot_ty, slot_arg) = match f.borrowed { + None => (quote!(Slot), quote!()), + Some(Borrowed::Shared) => ( + // For borrowed fields, create a `SelfRefSlot`, which after initialization + // turns into a `SelfRefDropGuard` instead of `DropGuard`. + // + // They're mostly the same, except that `SelfRefDropGuard` returns `&'field T` + // instead of `&'guard T` for let bindings; this allows it to be used to be + // used to initialize other fields. + // + // The soundness of doing so relies on fact that `__make_init` requires a + // higher-ranked trait bound on the closure. Within the closure (which is the + // caller of the generated slot projection functions here), it can make no + // assumptions on the lifetime except for those implied by the struct's bounds, + // and we have validated them in `generate_drop_check`. + quote!(SelfRefSlot), + quote!(#lt, ::pin_init::__internal::Shared, ), + ), + Some(Borrowed::Mutable) => ( + // For borrowed fields, create a `SelfRefSlot`, which after initialization + // turns into a `SelfRefDropGuard` instead of `DropGuard`. + // + // They're mostly the same, except that `SelfRefDropGuard` returns `&'field T` + // instead of `&'guard T` for let bindings; this allows it to be used to be + // used to initialize other fields. + // + // The soundness of doing so relies on fact that `__make_init` requires a + // higher-ranked trait bound on the closure. Within the closure (which is the + // caller of the generated slot projection functions here), it can make no + // assumptions on the lifetime except for those implied by the struct's bounds, + // and we have validated them in `generate_drop_check`. + quote!(SelfRefSlot), + quote!(#lt, ::pin_init::__internal::Mutable, ), + ), + }; + quote! { /// # Safety /// @@ -398,19 +1129,52 @@ fn generate_the_pin_data( #vis unsafe fn #field_name( self, slot: *mut #struct_name #ty_generics, - ) -> ::pin_init::__internal::Slot<::pin_init::__internal::#pin_marker, #ty> { + ) -> ::pin_init::__internal::#slot_ty<#slot_arg ::pin_init::__internal::#pin_marker, #ty> { + // CAST: `as _` is needed to convert types wrapped inside `SelfRef`. // SAFETY: // - If `#pin_marker` is `Pinned`, the corresponding field is structurally // pinned. // - Other safety requirements follows the safety requirement. - unsafe { ::pin_init::__internal::Slot::new(&raw mut (*slot).#field_name) } + // - If `#slot_ty` is `SelfRefSlot`, the lifetime `#lt` represents that of the + // field. + unsafe { ::pin_init::__internal::#slot_ty::new(&raw mut (*slot).#field_name as _) } } } }) .collect::(); + quote! { - // We declare this struct which will host all of the projection function for our type. It - // will be invariant over all generic parameters which are inherited from the struct. + // We declare this struct which will host all of the projection function for our type. + #[doc(hidden)] + #vis struct __PinDataLt #generics_with_field_lt + #whr + { + // Use `__DropCheck` to capture all lifetimes (including that of borrowed fields) and + // generics invariantly. + __phantom: ::core::marker::PhantomData<__DropCheck #ty_generics_with_field_lt> + } + + impl #impl_generics_with_lt ::core::clone::Clone for __PinDataLt #ty_generics_with_field_lt + #whr + { + fn clone(&self) -> Self { *self } + } + + impl #impl_generics_with_lt ::core::marker::Copy for __PinDataLt #ty_generics_with_field_lt + #whr + {} + + #[allow(dead_code)] // Some functions might never be used and private. + #[expect(clippy::missing_safety_doc)] + impl #impl_generics_with_lt __PinDataLt #ty_generics_with_field_lt + #whr + { + #field_accessors + } + + // Declare a type that serves as the entry point of interaction with the `pin_init!` macro. + // We use this type instead of defining methods directly on user's type to avoid possibility + // of name conflicts. #[doc(hidden)] #vis struct __ThePinData #generics #whr @@ -428,8 +1192,6 @@ fn generate_the_pin_data( #whr {} - #[allow(dead_code)] // Some functions might never be used and private. - #[expect(clippy::missing_safety_doc)] impl #impl_generics __ThePinData #ty_generics #whr { @@ -437,13 +1199,16 @@ fn generate_the_pin_data( #[inline(always)] #vis fn __make_closure<__F, __E>(self, f: __F) -> __F where - __F: FnOnce(*mut #struct_name #ty_generics) -> + __F: #field_lt_for_generics ::core::ops::FnOnce(*mut #struct_name #ty_generics, __PinDataLt #ty_generics_with_field_lt) -> ::core::result::Result<::pin_init::__internal::InitOk, __E>, { f } - #field_accessors + #[inline(always)] + fn __with_lt #field_lt_generics(self) -> __PinDataLt #ty_generics_with_field_lt { + __PinDataLt { __phantom: ::core::marker::PhantomData } + } } // SAFETY: We have added the correct projection functions above to `__ThePinData` and diff --git a/internal/src/util.rs b/internal/src/util.rs new file mode 100644 index 00000000..147b2a2c --- /dev/null +++ b/internal/src/util.rs @@ -0,0 +1,235 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + +use std::collections::BTreeSet; + +use proc_macro2::{Span, TokenStream}; +use quote::{quote, ToTokens}; +use syn::punctuated::Punctuated; +use syn::visit::Visit; +use syn::visit_mut::VisitMut; +use syn::{BoundLifetimes, GenericParam, Generics, Ident, LifetimeParam, Token}; +use syn::{Lifetime, Type}; + +pub(crate) trait TypeExt { + /// Check if the type includes macro invocations. + /// + /// Proc-macros cannot expand macros and peek into them, so if macro is involved sometimes special handling is required. + fn has_macro(&self) -> bool; + + /// Get the list of unbound lifetimes referenced by the type. + fn unbound_lifetimes(&self) -> BTreeSet<&Lifetime>; +} + +impl TypeExt for Type { + fn has_macro(&self) -> bool { + struct HasMacro(bool); + + impl<'ast> Visit<'ast> for HasMacro { + fn visit_macro(&mut self, _: &'ast syn::Macro) { + self.0 = true; + } + } + + let mut visitor = HasMacro(false); + visitor.visit_type(self); + visitor.0 + } + + fn unbound_lifetimes(&self) -> BTreeSet<&Lifetime> { + struct LifetimeVisitor<'a> { + bound: BTreeSet<&'a Lifetime>, + } + + impl<'a> Visit<'a> for LifetimeVisitor<'a> { + fn visit_lifetime(&mut self, lt: &'a Lifetime) { + if lt.ident == "static" { + return; + } + + if !self.bound.contains(lt) { + self.bound.insert(lt); + } + } + + fn visit_trait_bound(&mut self, bound: &'a syn::TraitBound) { + // In case the type includes a lifetime binder, e.g. `dyn for<'a> Foo`, + // temporarily remove them from `bound` if they're. + + let mut to_remove = Vec::new(); + if let Some(bound_lt) = &bound.lifetimes { + for lt in &bound_lt.lifetimes { + let GenericParam::Lifetime(lt) = lt else { + continue; + }; + if !self.bound.contains(&<.lifetime) { + to_remove.push(<.lifetime); + } + } + } + + self.visit_path(&bound.path); + + for lt in to_remove { + self.bound.remove(lt); + } + } + } + + let mut visitor = LifetimeVisitor { + bound: BTreeSet::new(), + }; + visitor.visit_type(self); + visitor.bound + } +} + +pub(crate) struct Binder { + pub bound: Vec, + pub value: T, +} + +impl Binder { + pub(crate) fn new(bound: Vec, value: T) -> Self { + Binder { bound, value } + } + + /// Obtain a `for<...>` that can be used to construct a higher-ranked trait bound. + pub(crate) fn for_bound(&self) -> BoundLifetimes { + BoundLifetimes { + for_token: Default::default(), + lt_token: Default::default(), + lifetimes: self + .bound + .iter() + .map(|lt| GenericParam::Lifetime(LifetimeParam::new(lt.clone()))) + .collect(), + gt_token: Default::default(), + } + } + + /// Similar to `for_bound`, but the number of lifetimes is padded to 4. + pub(crate) fn for_bound_4(&self) -> BoundLifetimes { + let mut lifetimes: Punctuated<_, _> = self + .bound + .iter() + .map(|lt| GenericParam::Lifetime(LifetimeParam::new(lt.clone()))) + .collect(); + while lifetimes.len() < 4 { + lifetimes.push(GenericParam::Lifetime(LifetimeParam::new(Lifetime::new( + &format!("'__lt{}", lifetimes.len()), + Span::mixed_site(), + )))); + } + BoundLifetimes { + for_token: Default::default(), + lt_token: Default::default(), + lifetimes, + gt_token: Default::default(), + } + } +} + +impl Binder { + pub(crate) fn instantiate(&self, lifetime: &Lifetime) -> Type { + // If there's no bound lifetimes, just return. + if self.bound.is_empty() { + return self.value.clone(); + } + + // If the type has macro, we cannot peek into it. Use some different approach to replace + // the type using GAT. + if self.value.has_macro() { + let bound = self.for_bound_4(); + let bound_lt = bound.lifetimes.iter(); + let ty = &self.value; + return syn::Type::Verbatim(quote!( + <::pin_init::__internal::ForLtImpl< + dyn #bound ::pin_init::__internal::WithLt4<#(#bound_lt,)* Of = #ty> + > as ::pin_init::__internal::ForLt4>::Of<#lifetime, #lifetime, #lifetime, #lifetime> + )); + } + + struct LifetimeReplacer<'a> { + to_replace: BTreeSet<&'a Lifetime>, + replacement: &'a Lifetime, + } + + impl VisitMut for LifetimeReplacer<'_> { + fn visit_lifetime_mut(&mut self, lt: &mut syn::Lifetime) { + if self.to_replace.contains(lt) { + *lt = self.replacement.clone(); + } + } + + fn visit_trait_bound_mut(&mut self, bound: &mut syn::TraitBound) { + // In case the type includes a lifetime binder, e.g. `dyn for<'a> Foo`, + // temporarily remove them from to_replace if they're. + + let mut removed = Vec::new(); + if let Some(bound_lt) = &bound.lifetimes { + for lt in &bound_lt.lifetimes { + let GenericParam::Lifetime(lt) = lt else { + continue; + }; + if let Some(lt) = self.to_replace.take(<.lifetime) { + removed.push(lt); + } + } + } + + self.visit_path_mut(&mut bound.path); + + for lt in removed { + self.to_replace.insert(lt); + } + } + } + + let mut ret = self.value.clone(); + LifetimeReplacer { + to_replace: self.bound.iter().collect(), + replacement: lifetime, + } + .visit_type_mut(&mut ret); + ret + } +} + +pub(crate) trait LifetimeExt { + /// Obtain a lifetime from a identifier. + /// + /// The created lifetime has the same span. + fn from_ident(ident: &Ident) -> Self; +} + +impl LifetimeExt for Lifetime { + fn from_ident(ident: &Ident) -> Self { + Lifetime { + apostrophe: ident.span(), + ident: ident.clone(), + } + } +} + +pub(crate) struct ForGenerics<'a>(&'a Generics); + +pub(crate) trait GenericsExt { + fn for_generics(&self) -> ForGenerics<'_>; +} + +impl GenericsExt for Generics { + fn for_generics(&self) -> ForGenerics<'_> { + ForGenerics(self) + } +} + +impl ToTokens for ForGenerics<'_> { + fn to_tokens(&self, tokens: &mut TokenStream) { + if self.0.params.is_empty() { + return; + } + + ::default().to_tokens(tokens); + self.0.split_for_impl().1.to_tokens(tokens); + } +} diff --git a/src/__internal.rs b/src/__internal.rs index 56dc655e..81955a49 100644 --- a/src/__internal.rs +++ b/src/__internal.rs @@ -5,8 +5,14 @@ //! These items must not be used outside of this crate and the pin-init-internal crate located at //! `../internal`. +use core::marker::PhantomPinned; + use super::*; +// Polyfill for the unstable `UnsafePinned` type. +#[repr(transparent)] +pub struct UnsafePinned(PhantomPinned, UnsafeCell); + /// Zero-sized type used to mark a type as invariant. /// /// This is a polyfill for the [unstable type] in the standard library of the same name. @@ -117,10 +123,15 @@ impl AllData { #[inline(always)] pub fn __make_closure(self, f: F) -> F where - F: FnOnce(*mut T) -> Result, + F: FnOnce(*mut T, Self) -> Result, { f } + + #[inline(always)] + pub fn __with_lt(self) -> Self { + self + } } // SAFETY: TODO. @@ -359,6 +370,169 @@ impl Drop for DropGuard { } } +pub struct Shared; +pub struct Mutable; + +/// Represent an uninitialized field in a pinned struct that will be referenced by other fields. +/// +/// # Invariants +/// +/// - `ptr` is valid, properly aligned and points to uninitialized and exclusively accessed memory +/// and will live longer than `'a`. +/// - If `P` is `Pinned`, then `ptr` is structurally pinned. +pub struct SelfRefSlot<'a, M, P, T: ?Sized> { + pub ptr: *mut T, + pub _phantom: PhantomData<(M, P, &'a mut T)>, +} + +impl<'a, M, P, T: ?Sized> SelfRefSlot<'a, M, P, T> { + /// # Safety + /// + /// - `ptr` is valid, properly aligned and points to uninitialized and exclusively accessed + /// memory and will live longer than `'a`. + /// - If `P` is `Pinned`, then `ptr` is structurally pinned. + #[inline] + pub unsafe fn new(ptr: *mut T) -> Self { + // INVARIANT: Per safety requirement. + Self { + ptr, + _phantom: PhantomData, + } + } + + /// Initialize the field by value. + #[inline] + pub fn write(self, value: T) -> SelfRefDropGuard<'a, M, P, T> + where + T: Sized, + { + // SAFETY: `self.ptr` is a valid and aligned pointer for write. + unsafe { self.ptr.write(value) } + // SAFETY: + // - `self.ptr` is valid, properly aligned and live longer than `'a` per type invariant. + // - `*self.ptr` is initialized above and the ownership is transferred to the guard. + // - If `P` is `Pinned`, `self.ptr` is pinned. + unsafe { SelfRefDropGuard::new(self.ptr) } + } +} + +impl<'a, M, T: ?Sized> SelfRefSlot<'a, M, Unpinned, T> { + /// Initialize the field. + #[inline] + pub fn init(self, init: impl Init) -> Result, E> { + // SAFETY: + // - `self.ptr` is valid and properly aligned. + // - when `Err` is returned, we also propagate the error without touching `slot`; + // also `self` is consumed so it cannot be touched further. + unsafe { init.__init(self.ptr)? }; + + // SAFETY: + // - `self.ptr` is valid, properly aligned and live longer than `'a` per type invariant. + // - `*self.ptr` is initialized above and the ownership is transferred to the guard. + Ok(unsafe { SelfRefDropGuard::new(self.ptr) }) + } +} + +impl<'a, M, T: ?Sized> SelfRefSlot<'a, M, Pinned, T> { + /// Initialize the field. + #[inline] + pub fn init( + self, + init: impl PinInit, + ) -> Result, E> { + // SAFETY: + // - `ptr` is valid + // - when `Err` is returned, we also propagate the error without touching `ptr`; + // also `self` is consumed so it cannot be touched further. + // - the drop guard will not hand out `&mut` (but only `Pin<&mut T>`) it has been dropped. + unsafe { init.__pinned_init(self.ptr)? }; + + // SAFETY: + // - `self.ptr` is valid, properly aligned and live longer than `'a` per type invariant. + // - `*self.ptr` is initialized above and the ownership is transferred to the guard. + Ok(unsafe { SelfRefDropGuard::new(self.ptr) }) + } +} +/// When a value of this type is dropped, it drops a `T`. +/// +/// Can be forgotten to prevent the drop. +/// +/// # Invariants +/// +/// - `ptr` is valid, properly aligned and live longer than `'a`. +/// - `*ptr` is initialized and owned by this guard. +/// - if `P` is `Pinned`, `ptr` is pinned. +pub struct SelfRefDropGuard<'a, M, P, T: ?Sized> { + ptr: *mut T, + phantom: PhantomData<(M, P, &'a mut T)>, +} + +impl<'a, M, P, T: ?Sized> SelfRefDropGuard<'a, M, P, T> { + /// Creates a drop guard and transfer the ownership of the pointer content. + /// + /// The ownership is only relinquished if the guard is forgotten via [`core::mem::forget`]. + /// + /// # Safety + /// + /// - `ptr` is valid, properly aligned and live longer than `'a`. + /// - `*ptr` is initialized, and the ownership is transferred to this guard. + /// - if `P` is `Pinned`, `ptr` is pinned. + #[inline] + pub unsafe fn new(ptr: *mut T) -> Self { + // INVARIANT: By safety requirement. + Self { + ptr, + phantom: PhantomData, + } + } +} + +impl<'a, T: ?Sized> SelfRefDropGuard<'a, Shared, Unpinned, T> { + /// Create a let binding for accessor use. + #[inline] + pub fn let_binding(&mut self) -> &'a T { + // SAFETY: Per type invariant. + unsafe { &*self.ptr } + } +} + +impl<'a, T: ?Sized> SelfRefDropGuard<'a, Shared, Pinned, T> { + /// Create a let binding for accessor use. + #[inline] + pub fn let_binding(&mut self) -> Pin<&'a T> { + // SAFETY: `self.ptr` is valid, properly aligned, live longer than `'a`, initialized, + // exclusively accessible and pinned per type invariant. + unsafe { Pin::new_unchecked(&*self.ptr) } + } +} + +impl<'a, T: ?Sized> SelfRefDropGuard<'a, Mutable, Unpinned, T> { + /// Create a let binding for accessor use. + #[inline] + pub fn let_binding(&mut self) -> &'a mut T { + // SAFETY: Per type invariant. + unsafe { &mut *self.ptr } + } +} + +impl<'a, T: ?Sized> SelfRefDropGuard<'a, Mutable, Pinned, T> { + /// Create a let binding for accessor use. + #[inline] + pub fn let_binding(&mut self) -> Pin<&'a mut T> { + // SAFETY: `self.ptr` is valid, properly aligned, live longer than `'a`, initialized, + // exclusively accessible and pinned per type invariant. + unsafe { Pin::new_unchecked(&mut *self.ptr) } + } +} + +impl Drop for SelfRefDropGuard<'_, M, P, T> { + #[inline] + fn drop(&mut self) { + // SAFETY: `self.ptr` is valid, properly aligned and `*self.ptr` is owned by this guard. + unsafe { ptr::drop_in_place(self.ptr) } + } +} + /// Token used by `PinnedDrop` to prevent calling the function without creating this unsafely /// created struct. This is needed, because the `drop` function is safe, but should not be called /// manually. @@ -402,3 +576,79 @@ unsafe impl PinInit for AlwaysFail { Err(()) } } + +/// Representation of types generic over 4 lifetimes. +/// +/// This type limits the maximum amount of self-referential lifetimes supported, as we have checks +/// that requires generalization over lifetime and it is unsound to substitute lifetime. 2 lifetimes +/// can easily happen, 3 lifetimes should be much rarer, and 4 or more lifetimes would be +/// exceedingly rare. +pub trait ForLt4 { + /// The type parameterized by the lifetime. + type Of<'a, 'b, 'c, 'd>; +} + +// This is a helper trait for implementation `ForLt4` to be able to use HRTB. +pub trait WithLt4<'a, 'b, 'c, 'd> { + type Of; +} + +pub struct ForLtImpl(PhantomData); + +impl WithLt4<'a, 'b, 'c, 'd>> ForLt4 for ForLtImpl { + type Of<'a, 'b, 'c, 'd> = >::Of; +} + +/// A wrapper for fields that reference other fields to block direct access. +/// +/// Use the higher-ranked lifetime facility to support this. Note that it is important that this is +/// *not* just a wrapper of `F::Of<'static>`, so we can implement `Send` and `Sync` only when things +/// are true for all lifetimes. +#[repr(transparent)] +pub struct SelfRef( + F::Of<'static, 'static, 'static, 'static>, + // Mark the type as `!Send` and `!Sync` so we can implement it manually. The auto trait + // implementation will cause `SelfRef` to be `Send`/`Sync` when only `'static` is, causing a + // soundness hole similar to that of specialization. + PhantomData<*mut ()>, + // Mark the type as `!Unpin`. This is not actually needed for this type, but it is used to ensure + // tha the containing type is not `Unpin` by auto trait implementation. + // + // In a case of + // ``` + // #[pin_data] + // struct Foo { + // a: &'b u32, + // b: u32, + // } + // ``` + // + // This requires the type to be `!Unpin` despite all field types are `Unpin`. The `b` field is not going + // to transformed, so we rely on `SelfRef` to be the type that is `!Unpin`. + PhantomPinned, +); + +// SAFETY: The bound ensures that `F::Of` is `Send` for all lifetime parameters. +unsafe impl Send for SelfRef where for<'a, 'b, 'c, 'd> F::Of<'a, 'b, 'c, 'd>: Send {} +// SAFETY: The bound ensures that `F::Of` is `Sync` for all lifetime parameters. +unsafe impl Sync for SelfRef where for<'a, 'b, 'c, 'd> F::Of<'a, 'b, 'c, 'd>: Sync {} + +pub struct LifetimeGuard<'a> { + _phantom: PhantomInvariantLifetime<'a>, +} + +impl<'a> LifetimeGuard<'a> { + #[inline(always)] + pub fn new(_: &'a PhantomInvariant<&'a mut T>) -> Self { + LifetimeGuard { + _phantom: PhantomInvariantLifetime::new(), + } + } +} + +impl<'a> Drop for LifetimeGuard<'a> { + #[inline(always)] + fn drop(&mut self) { + // Intentionally empty. See `generate_drop_check` in `pin_data.rs` for details. + } +} diff --git a/src/lib.rs b/src/lib.rs index fd40c8f2..1a4b309c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -871,6 +871,7 @@ macro_rules! assert_pinned { let _ = move |ptr: *mut $ty| unsafe { let data = <$ty as $crate::__internal::HasPinData>::__pin_data(); _ = data + .__with_lt() .$field(ptr) .init($crate::__internal::AlwaysFail::<$field_ty>::new()); }; diff --git a/tests/selfref_shorten.rs b/tests/selfref_shorten.rs new file mode 100644 index 00000000..01b2a52f --- /dev/null +++ b/tests/selfref_shorten.rs @@ -0,0 +1,26 @@ +use pin_init::*; + +#[pin_data] +struct SelfRef { + #[borrows(foo, bar)] + part: &'foo str, + foo: String, + bar: String, +} + +#[test] +fn self_ref() { + stack_pin_init!(let foo = pin_init!(SelfRef { + foo: "hello world".to_owned(), + bar: "hello world".to_owned(), + part: &foo[..5], + })); + + stack_pin_init!(let bar = pin_init!(SelfRef { + foo: "hello world".to_owned(), + bar: "hello world".to_owned(), + // In this case, we borrow from a field that lives longers. + // We're allowed to coerce it into a shorter-living lifetime. + part: &bar[..5], + })); +} diff --git a/tests/ui/compile-fail/pin_data/selfref_covariant_check.rs b/tests/ui/compile-fail/pin_data/selfref_covariant_check.rs new file mode 100644 index 00000000..d471822e --- /dev/null +++ b/tests/ui/compile-fail/pin_data/selfref_covariant_check.rs @@ -0,0 +1,9 @@ +use pin_init::*; + +#[pin_data] +struct SelfRef { + not_cov: Box bool + 'str>, + str: String, +} + +fn main() {} diff --git a/tests/ui/compile-fail/pin_data/selfref_covariant_check.stderr b/tests/ui/compile-fail/pin_data/selfref_covariant_check.stderr new file mode 100644 index 00000000..1bceaa19 --- /dev/null +++ b/tests/ui/compile-fail/pin_data/selfref_covariant_check.stderr @@ -0,0 +1,15 @@ +error: lifetime may not live long enough + --> tests/ui/compile-fail/pin_data/selfref_covariant_check.rs:5:14 + | +3 | #[pin_data] + | ----------- in this attribute macro expansion +4 | struct SelfRef { +5 | not_cov: Box bool + 'str>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | lifetime `'__short` defined here + | lifetime `'__long` defined here + | function was supposed to return data with lifetime `'__long` but it is returning data with lifetime `'__short` + | + = help: consider adding the following bound: `'__short: '__long` + = note: this error originates in the attribute macro `pin_data` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/compile-fail/pin_data/selfref_dropck.rs b/tests/ui/compile-fail/pin_data/selfref_dropck.rs new file mode 100644 index 00000000..6c7b9d47 --- /dev/null +++ b/tests/ui/compile-fail/pin_data/selfref_dropck.rs @@ -0,0 +1,34 @@ +use pin_init::*; + +#[pin_data] +struct WrongDropOrder { + b: u32, + ptr: &'b u32, +} + +struct PrintOnDrop<'a>(&'a str); + +impl<'a> Drop for PrintOnDrop<'a> { + fn drop(&mut self) { + println!("Dropping: {}", self.0); + } +} + +#[pin_data] +struct UnsoundImplied { + // Provides an implied bound that `a` outlives `b`! + ptr: &'b &'a (), + a: String, + cannot_refer_a: PrintOnDrop<'b>, + b: String, +} + +fn main() { + let _foo = Box::pin_init(pin_init!(UnsoundImplied { + ptr: &&(), + a: "hello".to_owned(), + cannot_refer_a: PrintOnDrop(a), + b: "world".to_owned(), + })) + .unwrap(); +} diff --git a/tests/ui/compile-fail/pin_data/selfref_dropck.stderr b/tests/ui/compile-fail/pin_data/selfref_dropck.stderr new file mode 100644 index 00000000..e4817361 --- /dev/null +++ b/tests/ui/compile-fail/pin_data/selfref_dropck.stderr @@ -0,0 +1,22 @@ +error: field `ptr` borrows `b`, but drops later + --> tests/ui/compile-fail/pin_data/selfref_dropck.rs:6:11 + | +6 | ptr: &'b u32, + | ^^ + +error[E0597]: `a` does not live long enough + --> tests/ui/compile-fail/pin_data/selfref_dropck.rs:21:5 + | +18 | struct UnsoundImplied { + | - + | | + | `a` dropped here while still borrowed + | borrow might be used here, when `_guard` is dropped and runs the `Drop` code for type `pin_init::__internal::LifetimeGuard` +... +21 | a: String, + | ^ + | | + | borrowed value does not live long enough + | binding `a` declared here + | + = note: values in a scope are dropped in the opposite order they are defined diff --git a/tests/ui/compile-fail/pin_data/selfref_invalid_attr.rs b/tests/ui/compile-fail/pin_data/selfref_invalid_attr.rs new file mode 100644 index 00000000..ad78ce83 --- /dev/null +++ b/tests/ui/compile-fail/pin_data/selfref_invalid_attr.rs @@ -0,0 +1,17 @@ +use pin_init::*; + +#[pin_data] +struct InvalidAttr<'a> { + #[covariant] + #[not_covariant] // Duplicate variance attribute + #[borrows(non_exist, mut non_exist_mut)] // Borrows non-existent fields + explicit: u32, + + implicit: &'non_exist u32, + + bound: &'a u32, + okay: &'b u32, + b: u32, +} + +fn main() {} diff --git a/tests/ui/compile-fail/pin_data/selfref_invalid_attr.stderr b/tests/ui/compile-fail/pin_data/selfref_invalid_attr.stderr new file mode 100644 index 00000000..e95526e0 --- /dev/null +++ b/tests/ui/compile-fail/pin_data/selfref_invalid_attr.stderr @@ -0,0 +1,49 @@ +error: variance marker can only be specified once + --> tests/ui/compile-fail/pin_data/selfref_invalid_attr.rs:6:7 + | +6 | #[not_covariant] // Duplicate variance attribute + | ^^^^^^^^^^^^^ + +error: `non_exist` is neither a lifetime in generics nor a field name + --> tests/ui/compile-fail/pin_data/selfref_invalid_attr.rs:7:15 + | +7 | #[borrows(non_exist, mut non_exist_mut)] // Borrows non-existent fields + | ^^^^^^^^^ + +error: `non_exist` is neither a lifetime in generics nor a field name + --> tests/ui/compile-fail/pin_data/selfref_invalid_attr.rs:10:16 + | +10 | implicit: &'non_exist u32, + | ^^^^^^^^^^ + +error: `non_exist_mut` is neither a lifetime in generics nor a field name + --> tests/ui/compile-fail/pin_data/selfref_invalid_attr.rs:7:30 + | +7 | #[borrows(non_exist, mut non_exist_mut)] // Borrows non-existent fields + | ^^^^^^^^^^^^^ + +error[E0261]: use of undeclared lifetime name `'non_exist` + --> tests/ui/compile-fail/pin_data/selfref_invalid_attr.rs:10:16 + | +10 | implicit: &'non_exist u32, + | ^^^^^^^^^^ undeclared lifetime + | +help: consider introducing lifetime `'non_exist` here + | + 4 | struct InvalidAttr<'non_exist, 'a> { + | +++++++++++ + +error[E0261]: use of undeclared lifetime name `'non_exist` + --> tests/ui/compile-fail/pin_data/selfref_invalid_attr.rs:10:16 + | +10 | implicit: &'non_exist u32, + | ^^^^^^^^^^ undeclared lifetime + | +help: consider introducing lifetime `'non_exist` here + | +10 | implicit<'non_exist>: &'non_exist u32, + | ++++++++++++ +help: consider introducing lifetime `'non_exist` here + | + 4 | struct InvalidAttr<'non_exist, 'a> { + | +++++++++++ diff --git a/tests/ui/compile-fail/pin_data/selfref_lifetime_specialize.rs b/tests/ui/compile-fail/pin_data/selfref_lifetime_specialize.rs new file mode 100644 index 00000000..442cdf43 --- /dev/null +++ b/tests/ui/compile-fail/pin_data/selfref_lifetime_specialize.rs @@ -0,0 +1,38 @@ +// Ensure that types that have impl that specialize on a single lifetime can be used to exploit +// pin-init. + +use std::marker::PhantomData; + +use pin_init::*; + +struct LtSpec<'a>(PhantomData<*const &'a u32>); +struct LtSpec2<'a, 'b>(PhantomData<*const &'a &'b u32>); + +unsafe impl Send for LtSpec<'static> {} +unsafe impl Sync for LtSpec<'static> {} +unsafe impl<'a> Send for LtSpec2<'a, 'a> {} +unsafe impl<'a> Sync for LtSpec2<'a, 'a> {} + +#[pin_data] +struct Foo { + lt_spec: LtSpec<'a>, + a: u32, +} + +#[pin_data] +struct Bar { + lt_spec2: LtSpec2<'a, 'b>, + a: u32, + b: u32, +} + +fn assert_send() {} +fn assert_sync() {} + +fn main() { + // All of the below checks must fail. + assert_send::(); + assert_sync::(); + assert_send::(); + assert_sync::(); +} diff --git a/tests/ui/compile-fail/pin_data/selfref_lifetime_specialize.stderr b/tests/ui/compile-fail/pin_data/selfref_lifetime_specialize.stderr new file mode 100644 index 00000000..9d9b5fa6 --- /dev/null +++ b/tests/ui/compile-fail/pin_data/selfref_lifetime_specialize.stderr @@ -0,0 +1,45 @@ +error: implementation of `Send` is not general enough + --> tests/ui/compile-fail/pin_data/selfref_lifetime_specialize.rs:34:5 + | +34 | assert_send::(); + | ^^^^^^^^^^^^^^^^^^^^ implementation of `Send` is not general enough + | + = note: `LtSpec<'0>` must implement `Send`, for any lifetime `'0`... + = note: ...but `Send` is actually implemented for the type `LtSpec<'static>` + +error: implementation of `Sync` is not general enough + --> tests/ui/compile-fail/pin_data/selfref_lifetime_specialize.rs:35:5 + | +35 | assert_sync::(); + | ^^^^^^^^^^^^^^^^^^^^ implementation of `Sync` is not general enough + | + = note: `LtSpec<'0>` must implement `Sync`, for any lifetime `'0`... + = note: ...but `Sync` is actually implemented for the type `LtSpec<'static>` + +error[E0308]: mismatched types + --> tests/ui/compile-fail/pin_data/selfref_lifetime_specialize.rs:36:5 + | +36 | assert_send::(); + | ^^^^^^^^^^^^^^^^^^^^ one type is more general than the other + | + = note: expected associated type `<(dyn for<'a, 'b, '__lt2, '__lt3> pin_init::__internal::WithLt4<'a, 'b, '__lt2, '__lt3, Of = LtSpec2<'a, 'b>> + 'static) as pin_init::__internal::WithLt4<'_, '_, '_, '_>>::Of` + found associated type `<(dyn for<'a, 'b, '__lt2, '__lt3> pin_init::__internal::WithLt4<'a, 'b, '__lt2, '__lt3, Of = LtSpec2<'a, 'b>> + 'static) as pin_init::__internal::WithLt4<'a, 'b, 'c, 'd>>::Of` +note: the lifetime requirement is introduced here + --> tests/ui/compile-fail/pin_data/selfref_lifetime_specialize.rs:29:19 + | +29 | fn assert_send() {} + | ^^^^ + +error[E0308]: mismatched types + --> tests/ui/compile-fail/pin_data/selfref_lifetime_specialize.rs:37:5 + | +37 | assert_sync::(); + | ^^^^^^^^^^^^^^^^^^^^ one type is more general than the other + | + = note: expected associated type `<(dyn for<'a, 'b, '__lt2, '__lt3> pin_init::__internal::WithLt4<'a, 'b, '__lt2, '__lt3, Of = LtSpec2<'a, 'b>> + 'static) as pin_init::__internal::WithLt4<'_, '_, '_, '_>>::Of` + found associated type `<(dyn for<'a, 'b, '__lt2, '__lt3> pin_init::__internal::WithLt4<'a, 'b, '__lt2, '__lt3, Of = LtSpec2<'a, 'b>> + 'static) as pin_init::__internal::WithLt4<'a, 'b, 'c, 'd>>::Of` +note: the lifetime requirement is introduced here + --> tests/ui/compile-fail/pin_data/selfref_lifetime_specialize.rs:30:19 + | +30 | fn assert_sync() {} + | ^^^^ diff --git a/tests/ui/compile-fail/pin_data/selfref_lifetime_specialize_unpin.rs b/tests/ui/compile-fail/pin_data/selfref_lifetime_specialize_unpin.rs new file mode 100644 index 00000000..30db2403 --- /dev/null +++ b/tests/ui/compile-fail/pin_data/selfref_lifetime_specialize_unpin.rs @@ -0,0 +1,31 @@ +// Ensure that types that have impl that specialize on a single lifetime can be used to exploit +// pin-init. Separate from selfref_lifetime_specialize.rs as somehow `Unpin` check suppresses `Send` +// and `Sync` errors. + +use std::marker::PhantomData; + +use pin_init::*; + +struct LtSpec<'a>(PhantomData<*const &'a u32>); +struct LtSpec2<'a, 'b>(PhantomData<*const &'a &'b u32>); + +#[pin_data] +struct Foo { + lt_spec: LtSpec<'a>, + a: u32, +} + +#[pin_data] +struct Bar { + lt_spec2: LtSpec2<'a, 'b>, + a: u32, + b: u32, +} + +fn assert_unpin() {} + +fn main() { + // All of the below checks must fail. + assert_unpin::(); + assert_unpin::(); +} diff --git a/tests/ui/compile-fail/pin_data/selfref_lifetime_specialize_unpin.stderr b/tests/ui/compile-fail/pin_data/selfref_lifetime_specialize_unpin.stderr new file mode 100644 index 00000000..1da729ab --- /dev/null +++ b/tests/ui/compile-fail/pin_data/selfref_lifetime_specialize_unpin.stderr @@ -0,0 +1,53 @@ +error[E0277]: `PhantomPinned` cannot be unpinned + --> tests/ui/compile-fail/pin_data/selfref_lifetime_specialize_unpin.rs:29:20 + | +29 | assert_unpin::(); + | ^^^ within `_::__Unpin<'_>`, the trait `Unpin` is not implemented for `PhantomPinned` + | + = note: consider using the `pin!` macro + consider using `Box::pin` if you need to access the pinned value outside of the current scope +note: required because it appears within the type `_::__Unpin<'_>` + --> tests/ui/compile-fail/pin_data/selfref_lifetime_specialize_unpin.rs:12:1 + | +12 | #[pin_data] + | ^^^^^^^^^^^ +note: required for `Foo` to implement `Unpin` + --> tests/ui/compile-fail/pin_data/selfref_lifetime_specialize_unpin.rs:12:1 + | +12 | #[pin_data] + | ^^^^^^^^^^^ unsatisfied trait bound introduced here +13 | struct Foo { + | ^^^ +note: required by a bound in `assert_unpin` + --> tests/ui/compile-fail/pin_data/selfref_lifetime_specialize_unpin.rs:25:20 + | +25 | fn assert_unpin() {} + | ^^^^^ required by this bound in `assert_unpin` + = note: this error originates in the attribute macro `pin_data` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: `PhantomPinned` cannot be unpinned + --> tests/ui/compile-fail/pin_data/selfref_lifetime_specialize_unpin.rs:30:20 + | +30 | assert_unpin::(); + | ^^^ within `_::__Unpin<'_>`, the trait `Unpin` is not implemented for `PhantomPinned` + | + = note: consider using the `pin!` macro + consider using `Box::pin` if you need to access the pinned value outside of the current scope +note: required because it appears within the type `_::__Unpin<'_>` + --> tests/ui/compile-fail/pin_data/selfref_lifetime_specialize_unpin.rs:18:1 + | +18 | #[pin_data] + | ^^^^^^^^^^^ +note: required for `Bar` to implement `Unpin` + --> tests/ui/compile-fail/pin_data/selfref_lifetime_specialize_unpin.rs:18:1 + | +18 | #[pin_data] + | ^^^^^^^^^^^ unsatisfied trait bound introduced here +19 | struct Bar { + | ^^^ +note: required by a bound in `assert_unpin` + --> tests/ui/compile-fail/pin_data/selfref_lifetime_specialize_unpin.rs:25:20 + | +25 | fn assert_unpin() {} + | ^^^^^ required by this bound in `assert_unpin` + = note: this error originates in the attribute macro `pin_data` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/compile-fail/pin_data/selfref_mut_borrow.rs b/tests/ui/compile-fail/pin_data/selfref_mut_borrow.rs new file mode 100644 index 00000000..995371af --- /dev/null +++ b/tests/ui/compile-fail/pin_data/selfref_mut_borrow.rs @@ -0,0 +1,28 @@ +use pin_init::*; + +#[pin_data] +struct SelfRef { + part: &'str str, + str: String, + #[borrows(mut mut_str)] + mut_part: &'mut_str mut str, + mut_str: String, +} + +fn use_self_ref() { + stack_pin_init!(let foo = pin_init!(SelfRef { + str: "hello world".to_owned(), + part: &str[..5], + mut_str: "hello world".to_owned(), + mut_part: &mut mut_str[..5], + })); + + // Should fail due to not accessible. + foo.as_mut().with_project(|proj| { + let _ = proj.mut_str; + }) +} + +fn main() { + use_self_ref(); +} diff --git a/tests/ui/compile-fail/pin_data/selfref_mut_borrow.stderr b/tests/ui/compile-fail/pin_data/selfref_mut_borrow.stderr new file mode 100644 index 00000000..190baab7 --- /dev/null +++ b/tests/ui/compile-fail/pin_data/selfref_mut_borrow.stderr @@ -0,0 +1,7 @@ +error[E0609]: no field `mut_str` on type `__ProjectionLt<'_, '_, '_>` + --> tests/ui/compile-fail/pin_data/selfref_mut_borrow.rs:22:22 + | +22 | let _ = proj.mut_str; + | ^^^^^^^ unknown field + | + = note: available fields are: `part`, `str`, `mut_part`, `___pin_phantom_data` diff --git a/tests/ui/compile-fail/pin_data/selfref_mut_borrowck.rs b/tests/ui/compile-fail/pin_data/selfref_mut_borrowck.rs new file mode 100644 index 00000000..b3cdc955 --- /dev/null +++ b/tests/ui/compile-fail/pin_data/selfref_mut_borrowck.rs @@ -0,0 +1,21 @@ +use pin_init::*; + +#[pin_data] +struct SelfRef { + part: &'str str, + #[borrows(mut str)] + mut_part: &'str mut str, + str: String, +} + +fn use_self_ref() { + stack_pin_init!(let foo = pin_init!(SelfRef { + str: "hello world".to_owned(), + part: &str[..5], + mut_part: &mut str[..5], + })); +} + +fn main() { + use_self_ref(); +} diff --git a/tests/ui/compile-fail/pin_data/selfref_mut_borrowck.stderr b/tests/ui/compile-fail/pin_data/selfref_mut_borrowck.stderr new file mode 100644 index 00000000..4ecc07d0 --- /dev/null +++ b/tests/ui/compile-fail/pin_data/selfref_mut_borrowck.stderr @@ -0,0 +1,15 @@ +error[E0502]: cannot borrow `*str` as mutable because it is also borrowed as immutable + --> tests/ui/compile-fail/pin_data/selfref_mut_borrowck.rs:15:24 + | +12 | stack_pin_init!(let foo = pin_init!(SelfRef { + | _______________________________- +13 | | str: "hello world".to_owned(), +14 | | part: &str[..5], + | | --- immutable borrow occurs here +15 | | mut_part: &mut str[..5], + | | ^^^ mutable borrow occurs here +16 | | })); + | | - + | | | + | |______has type `__PinDataLt<'1>` + | argument requires that `*str` is borrowed for `'1` diff --git a/tests/ui/compile-fail/pin_data/selfref_not_living_long_enough.rs b/tests/ui/compile-fail/pin_data/selfref_not_living_long_enough.rs new file mode 100644 index 00000000..703e2823 --- /dev/null +++ b/tests/ui/compile-fail/pin_data/selfref_not_living_long_enough.rs @@ -0,0 +1,19 @@ +use pin_init::*; + +#[pin_data] +struct SelfRef { + #[borrows(foo, bar)] + part: &'foo str, + foo: String, + bar: String, +} + +fn self_ref(outer: &str) { + stack_pin_init!(let foo = pin_init!(SelfRef { + foo: "hello world".to_owned(), + bar: "hello world".to_owned(), + part: &outer[..5], + })); +} + +fn main() {} diff --git a/tests/ui/compile-fail/pin_data/selfref_not_living_long_enough.stderr b/tests/ui/compile-fail/pin_data/selfref_not_living_long_enough.stderr new file mode 100644 index 00000000..bc856eb3 --- /dev/null +++ b/tests/ui/compile-fail/pin_data/selfref_not_living_long_enough.stderr @@ -0,0 +1,19 @@ +error[E0521]: borrowed data escapes outside of function + --> tests/ui/compile-fail/pin_data/selfref_not_living_long_enough.rs:12:31 + | +11 | fn self_ref(outer: &str) { + | ----- - let's call the lifetime of this reference `'1` + | | + | `outer` is a reference that is only valid in the function body +12 | stack_pin_init!(let foo = pin_init!(SelfRef { + | _______________________________^ +13 | | foo: "hello world".to_owned(), +14 | | bar: "hello world".to_owned(), +15 | | part: &outer[..5], +16 | | })); + | | ^ + | | | + | |______`outer` escapes the function body here + | argument requires that `'1` must outlive `'static` + | + = note: this error originates in the macro `pin_init` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/compile-fail/pin_data/selfref_project_mut.rs b/tests/ui/compile-fail/pin_data/selfref_project_mut.rs new file mode 100644 index 00000000..4dd6f02b --- /dev/null +++ b/tests/ui/compile-fail/pin_data/selfref_project_mut.rs @@ -0,0 +1,29 @@ +use pin_init::*; + +#[pin_data] +struct SelfRef { + part: &'str str, + str: String, + #[borrows(mut mut_str)] + mut_part: &'mut_str mut str, + mut_str: String, +} + +fn use_self_ref() { + stack_pin_init!(let foo = pin_init!(SelfRef { + str: "hello world".to_owned(), + part: &str[..5], + mut_str: "hello world".to_owned(), + mut_part: &mut mut_str[..5], + })); + + // Should fail due to reference not being mutable. + *foo.as_mut().project().part = "foo"; + + // Should fail due to reference not being mutable. + foo.as_mut().project().mut_part.make_ascii_uppercase(); +} + +fn main() { + use_self_ref(); +} diff --git a/tests/ui/compile-fail/pin_data/selfref_project_mut.stderr b/tests/ui/compile-fail/pin_data/selfref_project_mut.stderr new file mode 100644 index 00000000..57d88d84 --- /dev/null +++ b/tests/ui/compile-fail/pin_data/selfref_project_mut.stderr @@ -0,0 +1,11 @@ +error[E0594]: cannot assign to data in a `&` reference + --> tests/ui/compile-fail/pin_data/selfref_project_mut.rs:21:5 + | +21 | *foo.as_mut().project().part = "foo"; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot assign + +error[E0596]: cannot borrow data in a `&` reference as mutable + --> tests/ui/compile-fail/pin_data/selfref_project_mut.rs:24:5 + | +24 | foo.as_mut().project().mut_part.make_ascii_uppercase(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable diff --git a/tests/ui/compile-fail/pin_data/selfref_with_project.rs b/tests/ui/compile-fail/pin_data/selfref_with_project.rs new file mode 100644 index 00000000..7e63f679 --- /dev/null +++ b/tests/ui/compile-fail/pin_data/selfref_with_project.rs @@ -0,0 +1,28 @@ +use pin_init::*; + +#[pin_data] +struct SelfRef { + part: &'str str, + str: String, + #[borrows(mut mut_str)] + mut_part: &'mut_str mut str, + mut_str: String, +} + +fn use_self_ref() { + stack_pin_init!(let foo = pin_init!(SelfRef { + str: "hello world".to_owned(), + part: &str[..5], + mut_str: "hello world".to_owned(), + mut_part: &mut mut_str[..5], + })); + + let local = "hello world".to_owned(); + foo.as_mut().with_project(|proj| { + *proj.part = &local; + }); +} + +fn main() { + use_self_ref(); +} diff --git a/tests/ui/compile-fail/pin_data/selfref_with_project.stderr b/tests/ui/compile-fail/pin_data/selfref_with_project.stderr new file mode 100644 index 00000000..ee1ed52b --- /dev/null +++ b/tests/ui/compile-fail/pin_data/selfref_with_project.stderr @@ -0,0 +1,15 @@ +error[E0597]: `local` does not live long enough + --> tests/ui/compile-fail/pin_data/selfref_with_project.rs:22:23 + | +20 | let local = "hello world".to_owned(); + | ----- binding `local` declared here +21 | foo.as_mut().with_project(|proj| { + | ------ value captured here +22 | *proj.part = &local; + | --------------^^^^^ + | | | + | | borrowed value does not live long enough + | assignment requires that `local` is borrowed for `'static` +23 | }); +24 | } + | - `local` dropped here while still borrowed diff --git a/tests/ui/compile-fail/pin_data/twice.stderr b/tests/ui/compile-fail/pin_data/twice.stderr index abae43e5..4c56344a 100644 --- a/tests/ui/compile-fail/pin_data/twice.stderr +++ b/tests/ui/compile-fail/pin_data/twice.stderr @@ -1,14 +1,3 @@ -error[E0428]: the name `FooProjection` is defined multiple times - --> tests/ui/compile-fail/pin_data/twice.rs:4:1 - | -3 | #[pin_data] - | ----------- previous definition of the type `FooProjection` here -4 | #[pin_data] - | ^^^^^^^^^^^ `FooProjection` redefined here - | - = note: `FooProjection` must be defined only once in the type namespace of this module - = note: this error originates in the attribute macro `pin_data` (in Nightly builds, run with -Z macro-backtrace for more info) - error[E0119]: conflicting implementations of trait `pin_init::__internal::HasPinData` for type `Foo` --> tests/ui/compile-fail/pin_data/twice.rs:4:1 | @@ -39,12 +28,12 @@ error[E0592]: duplicate definitions with name `project` | = note: this error originates in the attribute macro `pin_data` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0308]: mismatched types +error[E0592]: duplicate definitions with name `with_project` --> tests/ui/compile-fail/pin_data/twice.rs:4:1 | +3 | #[pin_data] + | ----------- other definition for `with_project` 4 | #[pin_data] - | ^^^^^^^^^^^ expected `Pin<&mut usize>`, found `&mut usize` + | ^^^^^^^^^^^ duplicate definitions for `with_project` | - = note: expected struct `Pin<&mut usize>` - found mutable reference `&mut usize` = note: this error originates in the attribute macro `pin_data` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/expand/many_generics.expanded.rs b/tests/ui/expand/many_generics.expanded.rs index 5e182d65..b7af72f8 100644 --- a/tests/ui/expand/many_generics.expanded.rs +++ b/tests/ui/expand/many_generics.expanded.rs @@ -12,53 +12,101 @@ where r: &'b mut [&'a mut T; SIZE], _pin: PhantomPinned, } -/// Pin-projections of [`Foo`] -#[allow(dead_code, non_snake_case)] -#[doc(hidden)] -struct FooProjection<'__pin, 'a, 'b: 'a, T: Bar<'b> + ?Sized + 'a, const SIZE: usize = 0> -where - T: Bar<'a, 1>, -{ - array: &'__pin mut [u8; 1024 * 1024], - r: &'__pin mut &'b mut [&'a mut T; SIZE], - _pin: ::core::pin::Pin<&'__pin mut PhantomPinned>, - ___pin_phantom_data: ::core::marker::PhantomData<&'__pin mut ()>, -} -impl<'a, 'b: 'a, T: Bar<'b> + ?Sized + 'a, const SIZE: usize> Foo<'a, 'b, T, SIZE> -where - T: Bar<'a, 1>, -{ - /// Pin-projects all fields of `Self`. - /// - /// These fields are structurally pinned: - /// - `_pin` - /// - /// These fields are **not** structurally pinned: - /// - `array` - /// - `r` - #[inline] - fn project<'__pin>( - self: ::core::pin::Pin<&'__pin mut Self>, - ) -> FooProjection<'__pin, 'a, 'b, T, SIZE> { - let this = unsafe { ::core::pin::Pin::get_unchecked_mut(self) }; - FooProjection { - array: &mut this.array, - r: &mut this.r, - _pin: unsafe { ::core::pin::Pin::new_unchecked(&mut this._pin) }, - ___pin_phantom_data: ::core::marker::PhantomData, +const _: () = { + use Foo as __DropCheck; + /// Pin-projections of [`Foo`] + #[allow(dead_code, non_snake_case)] + #[doc(hidden)] + struct __Projection< + '__pin, + 'a, + 'b: 'a, + T: Bar<'b> + ?Sized + 'a, + const SIZE: usize = 0, + > + where + T: Bar<'a, 1>, + { + array: &'__pin mut [u8; 1024 * 1024], + r: &'__pin mut &'b mut [&'a mut T; SIZE], + _pin: ::core::pin::Pin<&'__pin mut PhantomPinned>, + ___pin_phantom_data: ::core::marker::PhantomData<&'__pin Foo<'a, 'b, T, SIZE>>, + } + /// Pin-projections of [`Foo`] + #[allow(dead_code, non_snake_case)] + #[doc(hidden)] + struct __ProjectionLt< + '__pin, + 'a, + 'b: 'a, + T: Bar<'b> + ?Sized + 'a, + const SIZE: usize = 0, + > + where + T: Bar<'a, 1>, + { + array: &'__pin mut [u8; 1024 * 1024], + r: &'__pin mut &'b mut [&'a mut T; SIZE], + _pin: ::core::pin::Pin<&'__pin mut PhantomPinned>, + ___pin_phantom_data: ::core::marker::PhantomData< + &'__pin mut __DropCheck<'a, 'b, T, SIZE>, + >, + } + impl<'a, 'b: 'a, T: Bar<'b> + ?Sized + 'a, const SIZE: usize> Foo<'a, 'b, T, SIZE> + where + T: Bar<'a, 1>, + { + /// Pin-projects all fields of `Self`. + /// + /// These fields are structurally pinned: + /// - `_pin` + /// + /// These fields are **not** structurally pinned: + /// - `array` + /// - `r` + #[inline] + fn project<'__pin>( + self: ::core::pin::Pin<&'__pin mut Self>, + ) -> __Projection<'__pin, 'a, 'b, T, SIZE> { + let this = unsafe { ::core::pin::Pin::get_unchecked_mut(self) }; + __Projection { + array: &mut this.array, + r: &mut this.r, + _pin: unsafe { ::core::pin::Pin::new_unchecked(&mut this._pin) }, + ___pin_phantom_data: ::core::marker::PhantomData, + } + } + /// Pin-projects all fields of `Self` with proper lifetime. + /// + /// These fields are structurally pinned: + /// - `_pin` + /// + /// These fields are **not** structurally pinned: + /// - `array` + /// - `r` + #[inline] + fn with_project<'__pin, R>( + self: ::core::pin::Pin<&'__pin mut Self>, + f: impl ::core::ops::FnOnce(__ProjectionLt<'__pin, 'a, 'b, T, SIZE>) -> R, + ) -> R { + let this = unsafe { ::core::pin::Pin::get_unchecked_mut(self) }; + f(__ProjectionLt { + array: &mut this.array, + r: &mut this.r, + _pin: unsafe { ::core::pin::Pin::new_unchecked(&mut this._pin) }, + ___pin_phantom_data: ::core::marker::PhantomData, + }) } } -} -const _: () = { #[doc(hidden)] - struct __ThePinData<'a, 'b: 'a, T: Bar<'b> + ?Sized + 'a, const SIZE: usize = 0> + struct __PinDataLt<'a, 'b: 'a, T: Bar<'b> + ?Sized + 'a, const SIZE: usize = 0> where T: Bar<'a, 1>, { - __phantom: ::pin_init::__internal::PhantomInvariant>, + __phantom: ::core::marker::PhantomData<__DropCheck<'a, 'b, T, SIZE>>, } impl<'a, 'b: 'a, T: Bar<'b> + ?Sized + 'a, const SIZE: usize> ::core::clone::Clone - for __ThePinData<'a, 'b, T, SIZE> + for __PinDataLt<'a, 'b, T, SIZE> where T: Bar<'a, 1>, { @@ -67,7 +115,7 @@ const _: () = { } } impl<'a, 'b: 'a, T: Bar<'b> + ?Sized + 'a, const SIZE: usize> ::core::marker::Copy - for __ThePinData<'a, 'b, T, SIZE> + for __PinDataLt<'a, 'b, T, SIZE> where T: Bar<'a, 1>, {} @@ -78,20 +126,10 @@ const _: () = { 'b: 'a, T: Bar<'b> + ?Sized + 'a, const SIZE: usize, - > __ThePinData<'a, 'b, T, SIZE> + > __PinDataLt<'a, 'b, T, SIZE> where T: Bar<'a, 1>, { - /// Type inference helper function. - #[inline(always)] - fn __make_closure<__F, __E>(self, f: __F) -> __F - where - __F: FnOnce( - *mut Foo<'a, 'b, T, SIZE>, - ) -> ::core::result::Result<::pin_init::__internal::InitOk, __E>, - { - f - } /// # Safety /// /// - `slot` is valid and properly aligned. @@ -107,7 +145,7 @@ const _: () = { ::pin_init::__internal::Unpinned, [u8; 1024 * 1024], > { - unsafe { ::pin_init::__internal::Slot::new(&raw mut (*slot).array) } + unsafe { ::pin_init::__internal::Slot::new(&raw mut (*slot).array as _) } } /// # Safety /// @@ -124,7 +162,7 @@ const _: () = { ::pin_init::__internal::Unpinned, &'b mut [&'a mut T; SIZE], > { - unsafe { ::pin_init::__internal::Slot::new(&raw mut (*slot).r) } + unsafe { ::pin_init::__internal::Slot::new(&raw mut (*slot).r as _) } } /// # Safety /// @@ -141,7 +179,55 @@ const _: () = { ::pin_init::__internal::Pinned, PhantomPinned, > { - unsafe { ::pin_init::__internal::Slot::new(&raw mut (*slot)._pin) } + unsafe { ::pin_init::__internal::Slot::new(&raw mut (*slot)._pin as _) } + } + } + #[doc(hidden)] + struct __ThePinData<'a, 'b: 'a, T: Bar<'b> + ?Sized + 'a, const SIZE: usize = 0> + where + T: Bar<'a, 1>, + { + __phantom: ::pin_init::__internal::PhantomInvariant>, + } + impl<'a, 'b: 'a, T: Bar<'b> + ?Sized + 'a, const SIZE: usize> ::core::clone::Clone + for __ThePinData<'a, 'b, T, SIZE> + where + T: Bar<'a, 1>, + { + fn clone(&self) -> Self { + *self + } + } + impl<'a, 'b: 'a, T: Bar<'b> + ?Sized + 'a, const SIZE: usize> ::core::marker::Copy + for __ThePinData<'a, 'b, T, SIZE> + where + T: Bar<'a, 1>, + {} + impl< + 'a, + 'b: 'a, + T: Bar<'b> + ?Sized + 'a, + const SIZE: usize, + > __ThePinData<'a, 'b, T, SIZE> + where + T: Bar<'a, 1>, + { + /// Type inference helper function. + #[inline(always)] + fn __make_closure<__F, __E>(self, f: __F) -> __F + where + __F: ::core::ops::FnOnce( + *mut Foo<'a, 'b, T, SIZE>, + __PinDataLt<'a, 'b, T, SIZE>, + ) -> ::core::result::Result<::pin_init::__internal::InitOk, __E>, + { + f + } + #[inline(always)] + fn __with_lt(self) -> __PinDataLt<'a, 'b, T, SIZE> { + __PinDataLt { + __phantom: ::core::marker::PhantomData, + } } } unsafe impl< diff --git a/tests/ui/expand/pin-data.expanded.rs b/tests/ui/expand/pin-data.expanded.rs index 12f1596f..537fac08 100644 --- a/tests/ui/expand/pin-data.expanded.rs +++ b/tests/ui/expand/pin-data.expanded.rs @@ -4,58 +4,76 @@ struct Foo { array: [u8; 1024 * 1024], _pin: PhantomPinned, } -/// Pin-projections of [`Foo`] -#[allow(dead_code, non_snake_case)] -#[doc(hidden)] -struct FooProjection<'__pin> { - array: &'__pin mut [u8; 1024 * 1024], - _pin: ::core::pin::Pin<&'__pin mut PhantomPinned>, - ___pin_phantom_data: ::core::marker::PhantomData<&'__pin mut ()>, -} -impl Foo { - /// Pin-projects all fields of `Self`. - /// - /// These fields are structurally pinned: - /// - `_pin` - /// - /// These fields are **not** structurally pinned: - /// - `array` - #[inline] - fn project<'__pin>( - self: ::core::pin::Pin<&'__pin mut Self>, - ) -> FooProjection<'__pin> { - let this = unsafe { ::core::pin::Pin::get_unchecked_mut(self) }; - FooProjection { - array: &mut this.array, - _pin: unsafe { ::core::pin::Pin::new_unchecked(&mut this._pin) }, - ___pin_phantom_data: ::core::marker::PhantomData, +const _: () = { + use Foo as __DropCheck; + /// Pin-projections of [`Foo`] + #[allow(dead_code, non_snake_case)] + #[doc(hidden)] + struct __Projection<'__pin> { + array: &'__pin mut [u8; 1024 * 1024], + _pin: ::core::pin::Pin<&'__pin mut PhantomPinned>, + ___pin_phantom_data: ::core::marker::PhantomData<&'__pin Foo>, + } + /// Pin-projections of [`Foo`] + #[allow(dead_code, non_snake_case)] + #[doc(hidden)] + struct __ProjectionLt<'__pin> { + array: &'__pin mut [u8; 1024 * 1024], + _pin: ::core::pin::Pin<&'__pin mut PhantomPinned>, + ___pin_phantom_data: ::core::marker::PhantomData<&'__pin mut __DropCheck>, + } + impl Foo { + /// Pin-projects all fields of `Self`. + /// + /// These fields are structurally pinned: + /// - `_pin` + /// + /// These fields are **not** structurally pinned: + /// - `array` + #[inline] + fn project<'__pin>( + self: ::core::pin::Pin<&'__pin mut Self>, + ) -> __Projection<'__pin> { + let this = unsafe { ::core::pin::Pin::get_unchecked_mut(self) }; + __Projection { + array: &mut this.array, + _pin: unsafe { ::core::pin::Pin::new_unchecked(&mut this._pin) }, + ___pin_phantom_data: ::core::marker::PhantomData, + } + } + /// Pin-projects all fields of `Self` with proper lifetime. + /// + /// These fields are structurally pinned: + /// - `_pin` + /// + /// These fields are **not** structurally pinned: + /// - `array` + #[inline] + fn with_project<'__pin, R>( + self: ::core::pin::Pin<&'__pin mut Self>, + f: impl ::core::ops::FnOnce(__ProjectionLt<'__pin>) -> R, + ) -> R { + let this = unsafe { ::core::pin::Pin::get_unchecked_mut(self) }; + f(__ProjectionLt { + array: &mut this.array, + _pin: unsafe { ::core::pin::Pin::new_unchecked(&mut this._pin) }, + ___pin_phantom_data: ::core::marker::PhantomData, + }) } } -} -const _: () = { #[doc(hidden)] - struct __ThePinData { - __phantom: ::pin_init::__internal::PhantomInvariant, + struct __PinDataLt { + __phantom: ::core::marker::PhantomData<__DropCheck>, } - impl ::core::clone::Clone for __ThePinData { + impl ::core::clone::Clone for __PinDataLt { fn clone(&self) -> Self { *self } } - impl ::core::marker::Copy for __ThePinData {} + impl ::core::marker::Copy for __PinDataLt {} #[allow(dead_code)] #[expect(clippy::missing_safety_doc)] - impl __ThePinData { - /// Type inference helper function. - #[inline(always)] - fn __make_closure<__F, __E>(self, f: __F) -> __F - where - __F: FnOnce( - *mut Foo, - ) -> ::core::result::Result<::pin_init::__internal::InitOk, __E>, - { - f - } + impl __PinDataLt { /// # Safety /// /// - `slot` is valid and properly aligned. @@ -71,7 +89,7 @@ const _: () = { ::pin_init::__internal::Unpinned, [u8; 1024 * 1024], > { - unsafe { ::pin_init::__internal::Slot::new(&raw mut (*slot).array) } + unsafe { ::pin_init::__internal::Slot::new(&raw mut (*slot).array as _) } } /// # Safety /// @@ -88,7 +106,36 @@ const _: () = { ::pin_init::__internal::Pinned, PhantomPinned, > { - unsafe { ::pin_init::__internal::Slot::new(&raw mut (*slot)._pin) } + unsafe { ::pin_init::__internal::Slot::new(&raw mut (*slot)._pin as _) } + } + } + #[doc(hidden)] + struct __ThePinData { + __phantom: ::pin_init::__internal::PhantomInvariant, + } + impl ::core::clone::Clone for __ThePinData { + fn clone(&self) -> Self { + *self + } + } + impl ::core::marker::Copy for __ThePinData {} + impl __ThePinData { + /// Type inference helper function. + #[inline(always)] + fn __make_closure<__F, __E>(self, f: __F) -> __F + where + __F: ::core::ops::FnOnce( + *mut Foo, + __PinDataLt, + ) -> ::core::result::Result<::pin_init::__internal::InitOk, __E>, + { + f + } + #[inline(always)] + fn __with_lt(self) -> __PinDataLt { + __PinDataLt { + __phantom: ::core::marker::PhantomData, + } } } unsafe impl ::pin_init::__internal::HasPinData for Foo { diff --git a/tests/ui/expand/pinned_drop.expanded.rs b/tests/ui/expand/pinned_drop.expanded.rs index 4cd7407d..63a0597f 100644 --- a/tests/ui/expand/pinned_drop.expanded.rs +++ b/tests/ui/expand/pinned_drop.expanded.rs @@ -4,58 +4,76 @@ struct Foo { array: [u8; 1024 * 1024], _pin: PhantomPinned, } -/// Pin-projections of [`Foo`] -#[allow(dead_code, non_snake_case)] -#[doc(hidden)] -struct FooProjection<'__pin> { - array: &'__pin mut [u8; 1024 * 1024], - _pin: ::core::pin::Pin<&'__pin mut PhantomPinned>, - ___pin_phantom_data: ::core::marker::PhantomData<&'__pin mut ()>, -} -impl Foo { - /// Pin-projects all fields of `Self`. - /// - /// These fields are structurally pinned: - /// - `_pin` - /// - /// These fields are **not** structurally pinned: - /// - `array` - #[inline] - fn project<'__pin>( - self: ::core::pin::Pin<&'__pin mut Self>, - ) -> FooProjection<'__pin> { - let this = unsafe { ::core::pin::Pin::get_unchecked_mut(self) }; - FooProjection { - array: &mut this.array, - _pin: unsafe { ::core::pin::Pin::new_unchecked(&mut this._pin) }, - ___pin_phantom_data: ::core::marker::PhantomData, +const _: () = { + use Foo as __DropCheck; + /// Pin-projections of [`Foo`] + #[allow(dead_code, non_snake_case)] + #[doc(hidden)] + struct __Projection<'__pin> { + array: &'__pin mut [u8; 1024 * 1024], + _pin: ::core::pin::Pin<&'__pin mut PhantomPinned>, + ___pin_phantom_data: ::core::marker::PhantomData<&'__pin Foo>, + } + /// Pin-projections of [`Foo`] + #[allow(dead_code, non_snake_case)] + #[doc(hidden)] + struct __ProjectionLt<'__pin> { + array: &'__pin mut [u8; 1024 * 1024], + _pin: ::core::pin::Pin<&'__pin mut PhantomPinned>, + ___pin_phantom_data: ::core::marker::PhantomData<&'__pin mut __DropCheck>, + } + impl Foo { + /// Pin-projects all fields of `Self`. + /// + /// These fields are structurally pinned: + /// - `_pin` + /// + /// These fields are **not** structurally pinned: + /// - `array` + #[inline] + fn project<'__pin>( + self: ::core::pin::Pin<&'__pin mut Self>, + ) -> __Projection<'__pin> { + let this = unsafe { ::core::pin::Pin::get_unchecked_mut(self) }; + __Projection { + array: &mut this.array, + _pin: unsafe { ::core::pin::Pin::new_unchecked(&mut this._pin) }, + ___pin_phantom_data: ::core::marker::PhantomData, + } + } + /// Pin-projects all fields of `Self` with proper lifetime. + /// + /// These fields are structurally pinned: + /// - `_pin` + /// + /// These fields are **not** structurally pinned: + /// - `array` + #[inline] + fn with_project<'__pin, R>( + self: ::core::pin::Pin<&'__pin mut Self>, + f: impl ::core::ops::FnOnce(__ProjectionLt<'__pin>) -> R, + ) -> R { + let this = unsafe { ::core::pin::Pin::get_unchecked_mut(self) }; + f(__ProjectionLt { + array: &mut this.array, + _pin: unsafe { ::core::pin::Pin::new_unchecked(&mut this._pin) }, + ___pin_phantom_data: ::core::marker::PhantomData, + }) } } -} -const _: () = { #[doc(hidden)] - struct __ThePinData { - __phantom: ::pin_init::__internal::PhantomInvariant, + struct __PinDataLt { + __phantom: ::core::marker::PhantomData<__DropCheck>, } - impl ::core::clone::Clone for __ThePinData { + impl ::core::clone::Clone for __PinDataLt { fn clone(&self) -> Self { *self } } - impl ::core::marker::Copy for __ThePinData {} + impl ::core::marker::Copy for __PinDataLt {} #[allow(dead_code)] #[expect(clippy::missing_safety_doc)] - impl __ThePinData { - /// Type inference helper function. - #[inline(always)] - fn __make_closure<__F, __E>(self, f: __F) -> __F - where - __F: FnOnce( - *mut Foo, - ) -> ::core::result::Result<::pin_init::__internal::InitOk, __E>, - { - f - } + impl __PinDataLt { /// # Safety /// /// - `slot` is valid and properly aligned. @@ -71,7 +89,7 @@ const _: () = { ::pin_init::__internal::Unpinned, [u8; 1024 * 1024], > { - unsafe { ::pin_init::__internal::Slot::new(&raw mut (*slot).array) } + unsafe { ::pin_init::__internal::Slot::new(&raw mut (*slot).array as _) } } /// # Safety /// @@ -88,7 +106,36 @@ const _: () = { ::pin_init::__internal::Pinned, PhantomPinned, > { - unsafe { ::pin_init::__internal::Slot::new(&raw mut (*slot)._pin) } + unsafe { ::pin_init::__internal::Slot::new(&raw mut (*slot)._pin as _) } + } + } + #[doc(hidden)] + struct __ThePinData { + __phantom: ::pin_init::__internal::PhantomInvariant, + } + impl ::core::clone::Clone for __ThePinData { + fn clone(&self) -> Self { + *self + } + } + impl ::core::marker::Copy for __ThePinData {} + impl __ThePinData { + /// Type inference helper function. + #[inline(always)] + fn __make_closure<__F, __E>(self, f: __F) -> __F + where + __F: ::core::ops::FnOnce( + *mut Foo, + __PinDataLt, + ) -> ::core::result::Result<::pin_init::__internal::InitOk, __E>, + { + f + } + #[inline(always)] + fn __with_lt(self) -> __PinDataLt { + __PinDataLt { + __phantom: ::core::marker::PhantomData, + } } } unsafe impl ::pin_init::__internal::HasPinData for Foo { diff --git a/tests/ui/expand/simple-init.expanded.rs b/tests/ui/expand/simple-init.expanded.rs index ee3d0096..02b95117 100644 --- a/tests/ui/expand/simple-init.expanded.rs +++ b/tests/ui/expand/simple-init.expanded.rs @@ -10,7 +10,7 @@ fn main() { .__make_closure::< _, ::core::convert::Infallible, - >(move |slot| { + >(move |slot, __data| { #[allow(unreachable_code, clippy::diverging_sub_expression)] let _ = || unsafe { ::core::ptr::write(slot, Foo {}) }; Ok(unsafe { ::pin_init::__internal::InitOk::new() }) @@ -18,7 +18,7 @@ fn main() { let init = move | slot, | -> ::core::result::Result<(), ::core::convert::Infallible> { - init(slot).map(|__InitOk| ()) + init(slot, __data.__with_lt()).map(|__InitOk| ()) }; unsafe { ::pin_init::init_from_closure::<_, ::core::convert::Infallible>(init) } };