diff --git a/CHANGELOG.md b/CHANGELOG.md index 137cf92a..7635c648 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 code at that point. - Make the `[try_][pin_]init!` macros expose initialized fields via a `let` binding as `&mut T` or `Pin<&mut T>` for later fields. +- `derive([Maybe]Zeroable)` now support tuple structs ## [0.0.10] - 2025-08-19 diff --git a/Cargo.lock b/Cargo.lock index a8c38a00..a99484bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -103,6 +103,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", + "syn", ] [[package]] @@ -117,9 +118,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.79" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] @@ -196,9 +197,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.58" +version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", diff --git a/README.md b/README.md index 74bbb4e0..6cee6ab1 100644 --- a/README.md +++ b/README.md @@ -135,7 +135,7 @@ struct DriverData { impl DriverData { fn new() -> impl PinInit { - try_pin_init!(Self { + pin_init!(Self { status <- CMutex::new(0), buffer: Box::init(pin_init::init_zeroed())?, }? Error) diff --git a/examples/linked_list.rs b/examples/linked_list.rs index f9e117c7..8445a589 100644 --- a/examples/linked_list.rs +++ b/examples/linked_list.rs @@ -6,7 +6,6 @@ use core::{ cell::Cell, - convert::Infallible, marker::PhantomPinned, pin::Pin, ptr::{self, NonNull}, @@ -31,31 +30,31 @@ pub struct ListHead { impl ListHead { #[inline] - pub fn new() -> impl PinInit { - try_pin_init!(&this in Self { + pub fn new() -> impl PinInit { + pin_init!(&this in Self { next: unsafe { Link::new_unchecked(this) }, prev: unsafe { Link::new_unchecked(this) }, pin: PhantomPinned, - }? Infallible) + }) } #[inline] #[allow(dead_code)] - pub fn insert_next(list: &ListHead) -> impl PinInit + '_ { - try_pin_init!(&this in Self { + pub fn insert_next(list: &ListHead) -> impl PinInit + '_ { + pin_init!(&this in Self { prev: list.next.prev().replace(unsafe { Link::new_unchecked(this)}), next: list.next.replace(unsafe { Link::new_unchecked(this)}), pin: PhantomPinned, - }? Infallible) + }) } #[inline] - pub fn insert_prev(list: &ListHead) -> impl PinInit + '_ { - try_pin_init!(&this in Self { + pub fn insert_prev(list: &ListHead) -> impl PinInit + '_ { + pin_init!(&this in Self { next: list.prev.next().replace(unsafe { Link::new_unchecked(this)}), prev: list.prev.replace(unsafe { Link::new_unchecked(this)}), pin: PhantomPinned, - }? Infallible) + }) } #[inline] diff --git a/examples/pthread_mutex.rs b/examples/pthread_mutex.rs index 49b004c8..4e082ec7 100644 --- a/examples/pthread_mutex.rs +++ b/examples/pthread_mutex.rs @@ -98,11 +98,11 @@ mod pthread_mtx { // SAFETY: mutex has been initialized unsafe { pin_init_from_closure(init) } } - try_pin_init!(Self { - data: UnsafeCell::new(data), - raw <- init_raw(), - pin: PhantomPinned, - }? Error) + pin_init!(Self { + data: UnsafeCell::new(data), + raw <- init_raw(), + pin: PhantomPinned, + }? Error) } #[allow(dead_code)] diff --git a/internal/Cargo.lock b/internal/Cargo.lock index 4fe4f0fe..245de5da 100644 --- a/internal/Cargo.lock +++ b/internal/Cargo.lock @@ -9,6 +9,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", + "syn", ] [[package]] @@ -22,9 +23,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.23" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" dependencies = [ "proc-macro2", ] @@ -44,6 +45,17 @@ version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" +[[package]] +name = "syn" +version = "2.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "unicode-ident" version = "1.0.8" diff --git a/internal/Cargo.toml b/internal/Cargo.toml index 3e375db1..d3af5351 100644 --- a/internal/Cargo.toml +++ b/internal/Cargo.toml @@ -15,6 +15,7 @@ proc-macro = true [dependencies] quote = "1.0" proc-macro2 = "1.0" +syn = { version = "2.0.86", features = ["full", "parsing", "visit-mut"] } [build-dependencies] rustc_version = "0.4" diff --git a/internal/src/helpers.rs b/internal/src/helpers.rs deleted file mode 100644 index 236f989a..00000000 --- a/internal/src/helpers.rs +++ /dev/null @@ -1,152 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 OR MIT - -#[cfg(not(kernel))] -use proc_macro2 as proc_macro; - -use proc_macro::{TokenStream, TokenTree}; - -/// Parsed generics. -/// -/// See the field documentation for an explanation what each of the fields represents. -/// -/// # Examples -/// -/// ```rust,ignore -/// # let input = todo!(); -/// let (Generics { decl_generics, impl_generics, ty_generics }, rest) = parse_generics(input); -/// quote! { -/// struct Foo<$($decl_generics)*> { -/// // ... -/// } -/// -/// impl<$impl_generics> Foo<$ty_generics> { -/// fn foo() { -/// // ... -/// } -/// } -/// } -/// ``` -pub(crate) struct Generics { - /// The generics with bounds and default values (e.g. `T: Clone, const N: usize = 0`). - /// - /// Use this on type definitions e.g. `struct Foo<$decl_generics> ...` (or `union`/`enum`). - pub(crate) decl_generics: Vec, - /// The generics with bounds (e.g. `T: Clone, const N: usize`). - /// - /// Use this on `impl` blocks e.g. `impl<$impl_generics> Trait for ...`. - pub(crate) impl_generics: Vec, - /// The generics without bounds and without default values (e.g. `T, N`). - /// - /// Use this when you use the type that is declared with these generics e.g. - /// `Foo<$ty_generics>`. - pub(crate) ty_generics: Vec, -} - -/// Parses the given `TokenStream` into `Generics` and the rest. -/// -/// The generics are not present in the rest, but a where clause might remain. -pub(crate) fn parse_generics(input: TokenStream) -> (Generics, Vec) { - // The generics with bounds and default values. - let mut decl_generics = vec![]; - // `impl_generics`, the declared generics with their bounds. - let mut impl_generics = vec![]; - // Only the names of the generics, without any bounds. - let mut ty_generics = vec![]; - // Tokens not related to the generics e.g. the `where` token and definition. - let mut rest = vec![]; - // The current level of `<`. - let mut nesting = 0; - let mut toks = input.into_iter(); - // If we are at the beginning of a generic parameter. - let mut at_start = true; - let mut skip_until_comma = false; - while let Some(tt) = toks.next() { - if nesting == 1 && matches!(&tt, TokenTree::Punct(p) if p.as_char() == '>') { - // Found the end of the generics. - break; - } else if nesting >= 1 { - decl_generics.push(tt.clone()); - } - match tt.clone() { - TokenTree::Punct(p) if p.as_char() == '<' => { - if nesting >= 1 && !skip_until_comma { - // This is inside of the generics and part of some bound. - impl_generics.push(tt); - } - nesting += 1; - } - TokenTree::Punct(p) if p.as_char() == '>' => { - // This is a parsing error, so we just end it here. - if nesting == 0 { - break; - } else { - nesting -= 1; - if nesting >= 1 && !skip_until_comma { - // We are still inside of the generics and part of some bound. - impl_generics.push(tt); - } - } - } - TokenTree::Punct(p) if skip_until_comma && p.as_char() == ',' => { - if nesting == 1 { - impl_generics.push(tt.clone()); - impl_generics.push(tt); - skip_until_comma = false; - } - } - _ if !skip_until_comma => { - match nesting { - // If we haven't entered the generics yet, we still want to keep these tokens. - 0 => rest.push(tt), - 1 => { - // Here depending on the token, it might be a generic variable name. - match tt.clone() { - TokenTree::Ident(i) if at_start && i.to_string() == "const" => { - let Some(name) = toks.next() else { - // Parsing error. - break; - }; - impl_generics.push(tt); - impl_generics.push(name.clone()); - ty_generics.push(name.clone()); - decl_generics.push(name); - at_start = false; - } - TokenTree::Ident(_) if at_start => { - impl_generics.push(tt.clone()); - ty_generics.push(tt); - at_start = false; - } - TokenTree::Punct(p) if p.as_char() == ',' => { - impl_generics.push(tt.clone()); - ty_generics.push(tt); - at_start = true; - } - // Lifetimes begin with `'`. - TokenTree::Punct(p) if p.as_char() == '\'' && at_start => { - impl_generics.push(tt.clone()); - ty_generics.push(tt); - } - // Generics can have default values, we skip these. - TokenTree::Punct(p) if p.as_char() == '=' => { - skip_until_comma = true; - } - _ => impl_generics.push(tt), - } - } - _ => impl_generics.push(tt), - } - } - _ => {} - } - } - rest.extend(toks); - ( - Generics { - impl_generics, - decl_generics, - ty_generics, - }, - rest, - ) -} diff --git a/internal/src/init.rs b/internal/src/init.rs new file mode 100644 index 00000000..e14bacc8 --- /dev/null +++ b/internal/src/init.rs @@ -0,0 +1,482 @@ +use proc_macro2::{Span, TokenStream}; +use quote::{format_ident, quote, quote_spanned}; +use syn::{ + braced, + parse::{End, Parse}, + parse_quote, + punctuated::Punctuated, + spanned::Spanned, + token, Attribute, Block, Expr, ExprCall, ExprPath, Ident, Path, Token, Type, +}; + +pub struct Initializer { + attrs: Vec, + this: Option, + path: Path, + brace_token: token::Brace, + fields: Punctuated, + rest: Option<(Token![..], Expr)>, + error: Option<(Token![?], Type)>, +} + +struct This { + _and_token: Token![&], + ident: Ident, + _in_token: Token![in], +} + +enum InitializerField { + Value { + ident: Ident, + value: Option<(Token![:], Expr)>, + }, + Init { + ident: Ident, + _left_arrow_token: Token![<-], + value: Expr, + }, + Code { + _underscore_token: Token![_], + _colon_token: Token![:], + block: Block, + }, +} + +impl InitializerField { + fn ident(&self) -> Option<&Ident> { + match self { + Self::Value { ident, .. } | Self::Init { ident, .. } => Some(ident), + Self::Code { .. } => None, + } + } +} + +enum InitializerAttribute { + DefaultError(DefaultErrorAttribute), +} + +struct DefaultErrorAttribute { + ty: Type, +} + +pub(crate) fn expand( + Initializer { + attrs, + this, + path, + brace_token, + fields, + rest, + error, + }: Initializer, + default_error: Option<&'static str>, + pinned: bool, +) -> TokenStream { + let mut errors = TokenStream::new(); + let mut error = error.map(|(_, err)| err); + if let Some(default_error) = attrs.iter().fold(None, |acc, attr| { + #[expect(irrefutable_let_patterns)] + if let InitializerAttribute::DefaultError(DefaultErrorAttribute { ty }) = attr { + Some(ty.clone()) + } else { + acc + } + }) { + error.get_or_insert(default_error); + } + if let Some(default_error) = default_error { + error.get_or_insert(syn::parse_str(default_error).unwrap()); + } + + let error = error.unwrap_or_else(|| { + errors.extend(quote_spanned!(brace_token.span.close()=> + ::core::compile_error!("expected `? ` after `}`"); + )); + parse_quote!(::core::convert::Infallible) + }); + let slot = format_ident!("slot"); + let (has_data_trait, data_trait, get_data, init_from_closure) = if pinned { + ( + format_ident!("HasPinData"), + format_ident!("PinData"), + format_ident!("__pin_data"), + format_ident!("pin_init_from_closure"), + ) + } else { + ( + format_ident!("HasInitData"), + format_ident!("InitData"), + format_ident!("__init_data"), + format_ident!("init_from_closure"), + ) + }; + let init_kind = get_init_kind(rest, &mut errors); + let zeroable_check = match init_kind { + InitKind::Normal => quote!(), + InitKind::Zeroing => quote! { + // The user specified `..Zeroable::zeroed()` at the end of the list of fields. + // Therefore we check if the struct implements `Zeroable` and then zero the memory. + // This allows us to also remove the check that all fields are present (since we + // already set the memory to zero and that is a valid bit pattern). + fn assert_zeroable(_: *mut T) + where T: ::pin_init::Zeroable + {} + // Ensure that the struct is indeed `Zeroable`. + assert_zeroable(#slot); + // SAFETY: The type implements `Zeroable` by the check above. + unsafe { ::core::ptr::write_bytes(#slot, 0, 1) }; + }, + }; + let this = match this { + None => quote!(), + Some(This { ident, .. }) => quote! { + // Create the `this` so it can be referenced by the user inside of the + // expressions creating the individual fields. + let #ident = unsafe { ::core::ptr::NonNull::new_unchecked(slot) }; + }, + }; + // `mixed_site` ensures that the data is not accessible to the user-controlled code. + let data = format_ident!("__data", span = Span::mixed_site()); + let init_fields = init_fields(&fields, pinned, &data, &slot); + let field_check = make_field_check(&fields, init_kind, &path); + quote! {{ + // We do not want to allow arbitrary returns, so we declare this type as the `Ok` return + // type and shadow it later when we insert the arbitrary user code. That way there will be + // no possibility of returning without `unsafe`. + struct __InitOk; + + // Get the data about fields from the supplied type. + // SAFETY: TODO + let #data = unsafe { + use ::pin_init::__internal::#has_data_trait; + // Can't use `<#path as #has_data_trait>::#get_data`, since the user is able to omit + // generics (which need to be present with that syntax). + #path::#get_data() + }; + // Ensure that `#data` really is of type `#data` and help with type inference: + let init = ::pin_init::__internal::#data_trait::make_closure::<_, __InitOk, #error>( + #data, + move |slot| { + { + // Shadow the structure so it cannot be used to return early. + struct __InitOk; + #zeroable_check + #this + #init_fields + #field_check + } + Ok(__InitOk) + } + ); + let init = move |slot| -> ::core::result::Result<(), #error> { + init(slot).map(|__InitOk| ()) + }; + // SAFETY: TODO + let init = unsafe { ::pin_init::#init_from_closure::<_, #error>(init) }; + init + }} +} + +enum InitKind { + Normal, + Zeroing, +} + +fn get_init_kind(rest: Option<(Token![..], Expr)>, errors: &mut TokenStream) -> InitKind { + let Some((dotdot, expr)) = rest else { + return InitKind::Normal; + }; + match &expr { + Expr::Call(ExprCall { func, args, .. }) if args.is_empty() => match &**func { + Expr::Path(ExprPath { + attrs, + qself: None, + path: + Path { + leading_colon: None, + segments, + }, + }) if attrs.is_empty() + && segments.len() == 2 + && segments[0].ident == "Zeroable" + && segments[0].arguments.is_none() + && segments[1].ident == "init_zeroed" + && segments[1].arguments.is_none() => + { + return InitKind::Zeroing; + } + _ => {} + }, + _ => {} + } + let span = quote!(#dotdot #expr).span(); + errors.extend(quote_spanned!(span=> + ::core::compile_error!("expected nothing or `..Zeroable::init_zeroed()`."); + )); + InitKind::Normal +} + +/// Generate the code that initializes the fields of the struct using the initializers in `field`. +fn init_fields( + fields: &Punctuated, + pinned: bool, + data: &Ident, + slot: &Ident, +) -> TokenStream { + let mut guards = vec![]; + let mut res = TokenStream::new(); + for field in fields { + let init = match field { + InitializerField::Value { ident, value } => { + let mut value_ident = ident.clone(); + let value_prep = value.as_ref().map(|value| &value.1).map(|value| { + // Setting the span of `value_ident` to `value`'s span improves error messages + // when the type of `value` is wrong. + value_ident.set_span(value.span()); + quote!(let #value_ident = #value;) + }); + // Again span for better diagnostics + let write = quote_spanned!(ident.span()=> ::core::ptr::write); + let accessor = if pinned { + let project_ident = format_ident!("__project_{ident}"); + quote! { + // SAFETY: TODO + unsafe { #data.#project_ident(&mut (*#slot).#ident) } + } + } else { + quote! { + // SAFETY: TODO + unsafe { &mut (*#slot).#ident } + } + }; + quote! { + { + #value_prep + // SAFETY: TODO + unsafe { #write(::core::ptr::addr_of_mut!((*#slot).#ident), #value_ident) }; + } + #[allow(unused_variables)] + let #ident = #accessor; + } + } + InitializerField::Init { ident, value, .. } => { + // Again span for better diagnostics + let init = format_ident!("init", span = value.span()); + if pinned { + let project_ident = format_ident!("__project_{ident}"); + quote! { + { + let #init = #value; + // SAFETY: + // - `slot` is valid, because we are inside of an initializer closure, we + // return when an error/panic occurs. + // - We also use `#data` to require the correct trait (`Init` or `PinInit`) + // for `#ident`. + unsafe { #data.#ident(::core::ptr::addr_of_mut!((*#slot).#ident), #init)? }; + } + // SAFETY: TODO + #[allow(unused_variables)] + let #ident = unsafe { #data.#project_ident(&mut (*#slot).#ident) }; + } + } else { + quote! { + { + let #init = #value; + // SAFETY: `slot` is valid, because we are inside of an initializer + // closure, we return when an error/panic occurs. + unsafe { + ::pin_init::Init::__init( + #init, + ::core::ptr::addr_of_mut!((*#slot).#ident), + )? + }; + } + // SAFETY: TODO + #[allow(unused_variables)] + let #ident = unsafe { &mut (*#slot).#ident }; + } + } + } + InitializerField::Code { block: value, .. } => quote!(#[allow(unused_braces)] #value), + }; + res.extend(init); + if let Some(ident) = field.ident() { + // `mixed_site` ensures that the guard is not accessible to the user-controlled code. + let guard = format_ident!("__{ident}_guard", span = Span::mixed_site()); + guards.push(guard.clone()); + res.extend(quote! { + // Create the drop guard: + // + // We rely on macro hygiene to make it impossible for users to access this local + // variable. + // SAFETY: We forget the guard later when initialization has succeeded. + let #guard = unsafe { + ::pin_init::__internal::DropGuard::new( + ::core::ptr::addr_of_mut!((*slot).#ident) + ) + }; + }); + } + } + quote! { + #res + // If execution reaches this point, all fields have been initialized. Therefore we can now + // dismiss the guards by forgetting them. + #(::core::mem::forget(#guards);)* + } +} + +/// Generate the check for ensuring that every field has been initialized. +fn make_field_check( + fields: &Punctuated, + init_kind: InitKind, + path: &Path, +) -> TokenStream { + let fields = fields.iter().filter_map(|f| f.ident()); + match init_kind { + InitKind::Normal => quote! { + // We use unreachable code to ensure that all fields have been mentioned exactly once, + // this struct initializer will still be type-checked and complain with a very natural + // error message if a field is forgotten/mentioned more than once. + #[allow(unreachable_code, clippy::diverging_sub_expression)] + // SAFETY: this code is never executed. + let _ = || unsafe { + ::core::ptr::write(slot, #path { + #( + #fields: ::core::panic!(), + )* + }) + }; + }, + InitKind::Zeroing => quote! { + // We use unreachable code to ensure that all fields have been mentioned at most once. + // Since the user specified `..Zeroable::zeroed()` at the end, all missing fields will + // be zeroed. This struct initializer will still be type-checked and complain with a + // very natural error message if a field is mentioned more than once, or doesn't exist. + #[allow(unreachable_code, clippy::diverging_sub_expression, unused_assignments)] + // SAFETY: this code is never executed. + let _ = || unsafe { + let mut zeroed = ::core::mem::zeroed(); + ::core::ptr::write(slot, zeroed); + zeroed = ::core::mem::zeroed(); + ::core::ptr::write(slot, #path { + #( + #fields: ::core::panic!(), + )* + ..zeroed + }) + }; + }, + } +} + +impl Parse for Initializer { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let attrs = input.call(Attribute::parse_outer)?; + let this = input.peek(Token![&]).then(|| input.parse()).transpose()?; + let path = input.parse()?; + let content; + let brace_token = braced!(content in input); + let mut fields = Punctuated::new(); + loop { + let lh = content.lookahead1(); + if lh.peek(End) || lh.peek(Token![..]) { + break; + } else if lh.peek(Ident) || lh.peek(Token![_]) { + fields.push_value(content.parse()?); + let lh = content.lookahead1(); + if lh.peek(End) { + break; + } else if lh.peek(Token![,]) { + fields.push_punct(content.parse()?); + } else { + return Err(lh.error()); + } + } else { + return Err(lh.error()); + } + } + let rest = content + .peek(Token![..]) + .then(|| Ok::<_, syn::Error>((content.parse()?, content.parse()?))) + .transpose()?; + let error = input + .peek(Token![?]) + .then(|| Ok::<_, syn::Error>((input.parse()?, input.parse()?))) + .transpose()?; + let attrs = attrs + .into_iter() + .map(|a| { + if a.path().is_ident("default_error") { + a.parse_args::() + .map(InitializerAttribute::DefaultError) + } else { + Err(syn::Error::new_spanned(a, "unknown initializer attribute")) + } + }) + .collect::, _>>()?; + Ok(Self { + attrs, + this, + path, + brace_token, + fields, + rest, + error, + }) + } +} + +impl Parse for DefaultErrorAttribute { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let ty = input.parse()?; + if !input.peek(End) { + return Err(input.error("expected end of input")); + } + Ok(Self { ty }) + } +} + +impl Parse for This { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + Ok(Self { + _and_token: input.parse()?, + ident: input.parse()?, + _in_token: input.parse()?, + }) + } +} + +impl Parse for InitializerField { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let lh = input.lookahead1(); + if lh.peek(Token![_]) { + Ok(Self::Code { + _underscore_token: input.parse()?, + _colon_token: input.parse()?, + block: input.parse()?, + }) + } else if lh.peek(Ident) { + let ident = input.parse()?; + let lh = input.lookahead1(); + if lh.peek(Token![<-]) { + Ok(Self::Init { + ident, + _left_arrow_token: input.parse()?, + value: input.parse()?, + }) + } else if lh.peek(Token![:]) { + Ok(Self::Value { + ident, + value: Some((input.parse()?, input.parse()?)), + }) + } else if lh.peek(Token![,]) || lh.peek(End) { + Ok(Self::Value { ident, value: None }) + } else { + Err(lh.error()) + } + } else { + Err(lh.error()) + } + } +} diff --git a/internal/src/lib.rs b/internal/src/lib.rs index 297b0129..360dd0ea 100644 --- a/internal/src/lib.rs +++ b/internal/src/lib.rs @@ -7,48 +7,61 @@ //! `pin-init` proc macros. #![cfg_attr(not(RUSTC_LINT_REASONS_IS_STABLE), feature(lint_reasons))] -// Allow `.into()` to convert -// - `proc_macro2::TokenStream` into `proc_macro::TokenStream` in the user-space version. -// - `proc_macro::TokenStream` into `proc_macro::TokenStream` in the kernel version. -// Clippy warns on this conversion, but it's required by the user-space version. -// -// Remove once we have `proc_macro2` in the kernel. -#![allow(clippy::useless_conversion)] // Documentation is done in the pin-init crate instead. #![allow(missing_docs)] use proc_macro::TokenStream; +use syn::parse_macro_input; -#[cfg(kernel)] -#[path = "../../../macros/quote.rs"] -#[macro_use] -#[cfg_attr(not(kernel), rustfmt::skip)] -mod quote; -#[cfg(not(kernel))] -#[macro_use] -extern crate quote; - -mod helpers; +mod init; mod pin_data; mod pinned_drop; mod zeroable; #[proc_macro_attribute] -pub fn pin_data(inner: TokenStream, item: TokenStream) -> TokenStream { - pin_data::pin_data(inner.into(), item.into()).into() +pub fn pin_data(args: TokenStream, input: TokenStream) -> TokenStream { + pin_data::pin_data( + parse_macro_input!(args as _), + parse_macro_input!(input as _), + ) + .into() } #[proc_macro_attribute] pub fn pinned_drop(args: TokenStream, input: TokenStream) -> TokenStream { - pinned_drop::pinned_drop(args.into(), input.into()).into() + pinned_drop::pinned_drop( + parse_macro_input!(args as _), + parse_macro_input!(input as _), + ) + .into() } #[proc_macro_derive(Zeroable)] pub fn derive_zeroable(input: TokenStream) -> TokenStream { - zeroable::derive(input.into()).into() + zeroable::derive(parse_macro_input!(input as _)).into() } #[proc_macro_derive(MaybeZeroable)] pub fn maybe_derive_zeroable(input: TokenStream) -> TokenStream { - zeroable::maybe_derive(input.into()).into() + zeroable::maybe_derive(parse_macro_input!(input as _)).into() +} + +#[proc_macro] +pub fn init(input: TokenStream) -> TokenStream { + init::expand( + parse_macro_input!(input as _), + Some("::core::convert::Infallible"), + false, + ) + .into() +} + +#[proc_macro] +pub fn pin_init(input: TokenStream) -> TokenStream { + init::expand( + parse_macro_input!(input as _), + Some("::core::convert::Infallible"), + true, + ) + .into() } diff --git a/internal/src/pin_data.rs b/internal/src/pin_data.rs index 87d4a7eb..83785cb0 100644 --- a/internal/src/pin_data.rs +++ b/internal/src/pin_data.rs @@ -1,132 +1,536 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT -#[cfg(not(kernel))] -use proc_macro2 as proc_macro; +use proc_macro2::TokenStream; +use quote::{format_ident, quote, quote_spanned}; +use syn::{ + parse::{End, Nothing, Parse}, + parse_quote, parse_quote_spanned, + spanned::Spanned, + visit_mut::VisitMut, + Field, Ident, Item, ItemStruct, PathSegment, Type, TypePath, WhereClause, +}; -use crate::helpers::{parse_generics, Generics}; -use proc_macro::{Group, Punct, Spacing, TokenStream, TokenTree}; +pub(crate) mod kw { + syn::custom_keyword!(PinnedDrop); +} + +pub(crate) enum Args { + Nothing(Nothing), + #[allow(dead_code)] + PinnedDrop(kw::PinnedDrop), +} + +impl Parse for Args { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let lh = input.lookahead1(); + if lh.peek(End) { + input.parse().map(Self::Nothing) + } else if lh.peek(kw::PinnedDrop) { + let res = input.parse().map(Self::PinnedDrop)?; + let lh = input.lookahead1(); + if lh.peek(End) { + Ok(res) + } else { + Err(lh.error()) + } + } else { + Err(lh.error()) + } + } +} -pub(crate) fn pin_data(args: TokenStream, input: TokenStream) -> TokenStream { - // This proc-macro only does some pre-parsing and then delegates the actual parsing to - // `pin_init::__pin_data!`. +pub(crate) fn pin_data(args: Args, input: Item) -> TokenStream { + let mut struct_ = match input { + Item::Struct(struct_) => struct_, + Item::Enum(_) | Item::Union(_) => { + return quote! { + ::core::compile_error!("`#[pin_data]` for now only supports structs"); + #input + }; + } + _ => { + return quote! { + ::core::compile_error!( + "`#[pin_data]` can only be applied to struct, enum and union defintions" + ); + #input + }; + } + }; + // The generics might contain the `Self` type. Since this macro will define a new type with the + // same generics and bounds, this poses a problem: `Self` will refer to the new type as opposed + // to this struct definition. Therefore we have to replace `Self` with the concrete name. + let mut replacer = { + let name = &struct_.ident; + let (_, ty_generics, _) = struct_.generics.split_for_impl(); + SelfReplacer(parse_quote!(#name #ty_generics)) + }; + replacer.visit_generics_mut(&mut struct_.generics); + replacer.visit_fields_mut(&mut struct_.fields); + + let mut errors = TokenStream::new(); + for field in &struct_.fields { + if !is_field_structurally_pinned(field) && is_phantom_pinned(&field.ty) { + let message = format!( + "The field `{}` of type `PhantomData` only has an effect \ + if it has the `#[pin]` attribute", + field.ident.as_ref().expect(""), + ); + errors.extend(quote_spanned!(field.span()=> ::core::compile_error!(#message);)); + } + } + + let unpin_impl = generate_unpin_impl(&struct_); + let drop_impl = generate_drop_impl(&struct_, args); + let projections = generate_projections(&struct_); + let the_pin_data = generate_the_pin_data(&struct_); + + strip_pin_annotations(&mut struct_); + + quote! { + #struct_ + #projections + // We put the rest into this const item, because it then will not be accessible to anything + // outside. + const _: () = { + #the_pin_data + #unpin_impl + #drop_impl + }; + #errors + } +} + +fn is_phantom_pinned(ty: &Type) -> bool { + match ty { + Type::Path(TypePath { qself: None, path }) => { + // Cannot possibly refer to `PhantomPinned` (except alias, but that's on the user). + if path.segments.len() > 3 { + return false; + } + // If there is a `::`, then the path needs to be `::core::marker::PhantomPinned` or + // `::std::marker::PhantomPinned`. + if path.leading_colon.is_some() && path.segments.len() != 3 { + return false; + } + let expected: Vec<&[&str]> = vec![&["PhantomPinned"], &["marker"], &["core", "std"]]; + for (actual, expected) in path.segments.iter().rev().zip(expected) { + if !actual.arguments.is_empty() || expected.iter().all(|e| actual.ident != e) { + return false; + } + } + true + } + _ => false, + } +} + +fn is_field_structurally_pinned(field: &Field) -> bool { + field.attrs.iter().any(|a| a.path().is_ident("pin")) +} + +fn generate_unpin_impl( + ItemStruct { + ident, + generics, + fields, + .. + }: &ItemStruct, +) -> TokenStream { + let generics_with_pinlt = { + let mut g = generics.clone(); + g.params.insert(0, parse_quote!('__pin)); + let _ = g.make_where_clause(); + g + }; let ( - Generics { - impl_generics, - decl_generics, - ty_generics, - }, - rest, - ) = parse_generics(input); - // The struct definition might contain the `Self` type. Since `__pin_data!` will define a new - // type with the same generics and bounds, this poses a problem, since `Self` will refer to the - // new type as opposed to this struct definition. Therefore we have to replace `Self` with the - // concrete name. - - // Errors that occur when replacing `Self` with `struct_name`. - let mut errs = TokenStream::new(); - // The name of the struct with ty_generics. - let struct_name = rest + impl_generics_with_pinlt, + ty_generics_with_pinlt, + Some(WhereClause { + where_token, + predicates, + }), + ) = generics_with_pinlt.split_for_impl() + else { + unreachable!() + }; + let (_, ty_generics, _) = generics.split_for_impl(); + let mut pinned_fields = fields .iter() - .skip_while(|tt| !matches!(tt, TokenTree::Ident(i) if i.to_string() == "struct")) - .nth(1) - .and_then(|tt| match tt { - TokenTree::Ident(_) => { - let tt = tt.clone(); - let mut res = vec![tt]; - if !ty_generics.is_empty() { - // We add this, so it is maximally compatible with e.g. `Self::CONST` which - // will be replaced by `StructName::<$generics>::CONST`. - res.push(TokenTree::Punct(Punct::new(':', Spacing::Joint))); - res.push(TokenTree::Punct(Punct::new(':', Spacing::Alone))); - res.push(TokenTree::Punct(Punct::new('<', Spacing::Alone))); - res.extend(ty_generics.iter().cloned()); - res.push(TokenTree::Punct(Punct::new('>', Spacing::Alone))); + .filter(|f| is_field_structurally_pinned(f)) + .cloned() + .collect::>(); + for field in &mut pinned_fields { + field.attrs.retain(|a| !a.path().is_ident("pin")); + } + 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`. + #[allow(dead_code)] // The fields below are never used. + struct __Unpin #generics_with_pinlt + #where_token + #predicates + { + __phantom_pin: ::core::marker::PhantomData &'__pin ()>, + __phantom: ::core::marker::PhantomData< + fn(#ident #ty_generics) -> #ident #ty_generics + >, + #(#pinned_fields),* + } + + #[doc(hidden)] + impl #impl_generics_with_pinlt ::core::marker::Unpin for #ident #ty_generics + #where_token + __Unpin #ty_generics_with_pinlt: ::core::marker::Unpin, + #predicates + {} + } +} + +fn generate_drop_impl( + ItemStruct { + ident, generics, .. + }: &syn::ItemStruct, + args: Args, +) -> TokenStream { + let (impl_generics, ty_generics, whr) = generics.split_for_impl(); + let has_pinned_drop = matches!(args, Args::PinnedDrop(_)); + // We need to disallow normal `Drop` implementation, the exact behavior depends on whether + // `PinnedDrop` was specified in `args`. + if has_pinned_drop { + // When `PinnedDrop` was specified we just implement `Drop` and delegate. + quote! { + impl #impl_generics ::core::ops::Drop for #ident #ty_generics + #whr + { + fn drop(&mut self) { + // SAFETY: Since this is a destructor, `self` will not move after this function + // terminates, since it is inaccessible. + let pinned = unsafe { ::core::pin::Pin::new_unchecked(self) }; + // SAFETY: Since this is a drop function, we can create this token to call the + // pinned destructor of this type. + let token = unsafe { ::pin_init::__internal::OnlyCallFromDrop::new() }; + ::pin_init::PinnedDrop::drop(pinned, token); } - Some(res) } - _ => None, - }) - .unwrap_or_else(|| { - // If we did not find the name of the struct then we will use `Self` as the replacement - // and add a compile error to ensure it does not compile. - errs.extend( - "::core::compile_error!(\"Could not locate type name.\");" - .parse::() - .unwrap(), - ); - "Self".parse::().unwrap().into_iter().collect() - }); - let impl_generics = impl_generics - .into_iter() - .flat_map(|tt| replace_self_and_deny_type_defs(&struct_name, tt, &mut errs)) - .collect::>(); - let mut rest = rest - .into_iter() - .flat_map(|tt| { - // We ignore top level `struct` tokens, since they would emit a compile error. - if matches!(&tt, TokenTree::Ident(i) if i.to_string() == "struct") { - vec![tt] + } + } else { + // When no `PinnedDrop` was specified, then we have to prevent implementing drop. + quote! { + // We prevent this by creating a trait that will be implemented for all types implementing + // `Drop`. Additionally we will implement this trait for the struct leading to a conflict, + // if it also implements `Drop` + trait MustNotImplDrop {} + #[expect(drop_bounds)] + impl MustNotImplDrop for T {} + impl #impl_generics MustNotImplDrop for #ident #ty_generics + #whr + {} + // We also take care to prevent users from writing a useless `PinnedDrop` implementation. + // They might implement `PinnedDrop` correctly for the struct, but forget to give + // `PinnedDrop` as the parameter to `#[pin_data]`. + #[expect(non_camel_case_types)] + trait UselessPinnedDropImpl_you_need_to_specify_PinnedDrop {} + impl + UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for T {} + impl #impl_generics + UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for #ident #ty_generics + #whr + {} + } + } +} + +fn generate_projections( + ItemStruct { + vis, + ident, + generics, + fields, + .. + }: &ItemStruct, +) -> TokenStream { + let (og_impl_gen, og_ty_gen, _) = generics.split_for_impl(); + let mut generics = generics.clone(); + generics.params.insert(0, parse_quote!('__pin)); + let (_, ty_gen, whr) = generics.split_for_impl(); + let projection = format_ident!("{ident}Projection"); + let this = format_ident!("this"); + + let (fields_decl, fields_proj) = collect_tuple(fields.iter().map( + |f @ Field { + vis, + ident, + ty, + attrs, + .. + }| { + let mut attrs = attrs.clone(); + attrs.retain(|a| !a.path().is_ident("pin")); + let mut no_doc_attrs = attrs.clone(); + no_doc_attrs.retain(|a| !a.path().is_ident("doc")); + let ident = ident + .as_ref() + .expect("only structs with named fields are supported"); + if is_field_structurally_pinned(f) { + ( + quote!( + #(#attrs)* + #vis #ident: ::core::pin::Pin<&'__pin mut #ty>, + ), + quote!( + #(#no_doc_attrs)* + // SAFETY: this field is structurally pinned. + #ident: unsafe { ::core::pin::Pin::new_unchecked(&mut #this.#ident) }, + ), + ) } else { - replace_self_and_deny_type_defs(&struct_name, tt, &mut errs) + ( + quote!( + #(#attrs)* + #vis #ident: &'__pin mut #ty, + ), + quote!( + #(#no_doc_attrs)* + #ident: &mut #this.#ident, + ), + ) } - }) - .collect::>(); - // This should be the body of the struct `{...}`. - let last = rest.pop(); - let mut quoted = quote!(::pin_init::__pin_data! { - parse_input: - @args(#args), - @sig(#(#rest)*), - @impl_generics(#(#impl_generics)*), - @ty_generics(#(#ty_generics)*), - @decl_generics(#(#decl_generics)*), - @body(#last), - }); - quoted.extend(errs); - quoted + }, + )); + let structurally_pinned_fields_docs = fields + .iter() + .filter(|f| is_field_structurally_pinned(f)) + .map(|Field { ident, .. }| { + let doc = format!(" - `{}`", ident.as_ref().expect("")); + quote!(#[doc = #doc]) + }); + let not_structurally_pinned_fields_docs = fields + .iter() + .filter(|f| !is_field_structurally_pinned(f)) + .map(|Field { ident, .. }| { + let doc = format!(" - `{}`", ident.as_ref().expect("")); + quote!(#[doc = #doc]) + }); + let docs = format!(" Pin-projections of [`{ident}`]"); + quote! { + #[doc = #docs] + #[allow(dead_code)] + #[doc(hidden)] + #vis struct #projection #generics { + #(#fields_decl)* + ___pin_phantom_data: ::core::marker::PhantomData<&'__pin mut ()>, + } + + impl #og_impl_gen #ident #og_ty_gen + #whr + { + /// Pin-projects all fields of `Self`. + /// + /// These fields are structurally pinned: + #(#structurally_pinned_fields_docs)* + /// + /// These fields are **not** structurally pinned: + #(#not_structurally_pinned_fields_docs)* + #[inline] + #vis fn project<'__pin>( + self: ::core::pin::Pin<&'__pin mut Self>, + ) -> #projection #ty_gen { + // SAFETY: we only give access to `&mut` for fields not structurally pinned. + let #this = unsafe { ::core::pin::Pin::get_unchecked_mut(self) }; + #projection { + #(#fields_proj)* + ___pin_phantom_data: ::core::marker::PhantomData, + } + } + } + } } -/// Replaces `Self` with `struct_name` and errors on `enum`, `trait`, `struct` `union` and `impl` -/// keywords. -/// -/// The error is appended to `errs` to allow normal parsing to continue. -fn replace_self_and_deny_type_defs( - struct_name: &Vec, - tt: TokenTree, - errs: &mut TokenStream, -) -> Vec { - match tt { - TokenTree::Ident(ref i) - if i.to_string() == "enum" - || i.to_string() == "trait" - || i.to_string() == "struct" - || i.to_string() == "union" - || i.to_string() == "impl" => +fn generate_the_pin_data( + ItemStruct { + generics, + fields, + ident, + vis, + .. + }: &syn::ItemStruct, +) -> TokenStream { + 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, then it must be initialized via `PinInit`, if it is + // not structurally pinned, then it can be initialized via `Init`. + // + // The functions are `unsafe` to prevent accidentally calling them. + fn handle_field( + Field { + vis, + ident, + ty, + attrs, + .. + }: &Field, + struct_ident: &Ident, + pinned: bool, + ) -> TokenStream { + let mut attrs = attrs.clone(); + attrs.retain(|a| !a.path().is_ident("pin")); + let ident = ident + .as_ref() + .expect("only structs with named fields are supported"); + let project_ident = format_ident!("__project_{ident}"); + let (init_ty, init_fn, project_ty, project_body, pin_safety) = if pinned { + ( + quote!(PinInit), + quote!(__pinned_init), + quote!(::core::pin::Pin<&'__slot mut #ty>), + // SAFETY: this field is structurally pinned. + quote!(unsafe { ::core::pin::Pin::new_unchecked(slot) }), + quote!( + #[doc = " - `slot` will not move until it is dropped, i.e. it will be pinned."] + ), + ) + } else { + ( + quote!(Init), + quote!(__init), + quote!(&'__slot mut #ty), + quote!(slot), + quote!(), + ) + }; + let slot_safety = format!( + " `slot` points at the field `{ident}` inside of `{struct_ident}`, which is pinned.", + ); + quote! { + /// # Safety + /// + /// - `slot` is a valid pointer to uninitialized memory. + /// - the caller does not touch `slot` when `Err` is returned, they are only permitted + /// to deallocate. + #pin_safety + #(#attrs)* + #vis unsafe fn #ident( + self, + slot: *mut #ty, + init: impl ::pin_init::#init_ty<#ty, E>, + ) -> ::core::result::Result<(), E> { + // SAFETY: this function has the same safety requirements as the __init function + // called below. + unsafe { ::pin_init::#init_ty::#init_fn(init, slot) } + } + + /// # Safety + /// + #[doc = #slot_safety] + #(#attrs)* + #vis unsafe fn #project_ident<'__slot>( + self, + slot: &'__slot mut #ty, + ) -> #project_ty { + #project_body + } + } + } + + let field_accessors = fields + .iter() + .map(|f| handle_field(f, ident, is_field_structurally_pinned(f))) + .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. + #[doc(hidden)] + #vis struct __ThePinData #generics + #whr { - errs.extend( - format!( - "::core::compile_error!(\"Cannot use `{i}` inside of struct definition with \ - `#[pin_data]`.\");" - ) - .parse::() - .unwrap() - .into_iter() - .map(|mut tok| { - tok.set_span(tt.span()); - tok - }), - ); - vec![tt] - } - TokenTree::Ident(i) if i.to_string() == "Self" => struct_name.clone(), - TokenTree::Literal(_) | TokenTree::Punct(_) | TokenTree::Ident(_) => vec![tt], - TokenTree::Group(g) => vec![TokenTree::Group(Group::new( - g.delimiter(), - g.stream() - .into_iter() - .flat_map(|tt| replace_self_and_deny_type_defs(struct_name, tt, errs)) - .collect(), - ))], + __phantom: ::core::marker::PhantomData< + fn(#ident #ty_generics) -> #ident #ty_generics + >, + } + + impl #impl_generics ::core::clone::Clone for __ThePinData #ty_generics + #whr + { + fn clone(&self) -> Self { *self } + } + + impl #impl_generics ::core::marker::Copy for __ThePinData #ty_generics + #whr + {} + + #[allow(dead_code)] // Some functions might never be used and private. + #[expect(clippy::missing_safety_doc)] + impl #impl_generics __ThePinData #ty_generics + #whr + { + #field_accessors + } + + // SAFETY: We have added the correct projection functions above to `__ThePinData` and + // we also use the least restrictive generics possible. + unsafe impl #impl_generics ::pin_init::__internal::HasPinData for #ident #ty_generics + #whr + { + type PinData = __ThePinData #ty_generics; + + unsafe fn __pin_data() -> Self::PinData { + __ThePinData { __phantom: ::core::marker::PhantomData } + } + } + + // SAFETY: TODO + unsafe impl #impl_generics ::pin_init::__internal::PinData for __ThePinData #ty_generics + #whr + { + type Datee = #ident #ty_generics; + } + } +} + +fn strip_pin_annotations(struct_: &mut syn::ItemStruct) { + for field in &mut struct_.fields { + field.attrs.retain(|a| !a.path().is_ident("pin")); + } +} + +struct SelfReplacer(PathSegment); + +impl VisitMut for SelfReplacer { + fn visit_path_mut(&mut self, i: &mut syn::Path) { + if i.is_ident("Self") { + let span = i.span(); + let seg = &self.0; + *i = parse_quote_spanned!(span=> #seg); + } else { + syn::visit_mut::visit_path_mut(self, i); + } + } + + fn visit_path_segment_mut(&mut self, seg: &mut PathSegment) { + if seg.ident == "Self" { + let span = seg.span(); + let this = &self.0; + *seg = parse_quote_spanned!(span=> #this); + } else { + syn::visit_mut::visit_path_segment_mut(self, seg); + } + } + + fn visit_item_mut(&mut self, _: &mut Item) { + // Do not descend into items, since items reset/change what `Self` refers to. + } +} + +// replace with `.collect()` once MSRV is above 1.79 +fn collect_tuple(iter: impl Iterator) -> (Vec, Vec) { + let mut res_a = vec![]; + let mut res_b = vec![]; + for (a, b) in iter { + res_a.push(a); + res_b.push(b); } + (res_a, res_b) } diff --git a/internal/src/pinned_drop.rs b/internal/src/pinned_drop.rs index c4ca7a70..4df2cb99 100644 --- a/internal/src/pinned_drop.rs +++ b/internal/src/pinned_drop.rs @@ -1,51 +1,56 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT -#[cfg(not(kernel))] -use proc_macro2 as proc_macro; +use proc_macro2::TokenStream; +use quote::{quote, quote_spanned}; +use syn::{parse::Nothing, parse_quote, spanned::Spanned, ImplItem, ItemImpl, Token}; -use proc_macro::{TokenStream, TokenTree}; - -pub(crate) fn pinned_drop(_args: TokenStream, input: TokenStream) -> TokenStream { - let mut toks = input.into_iter().collect::>(); - assert!(!toks.is_empty()); - // Ensure that we have an `impl` item. - assert!(matches!(&toks[0], TokenTree::Ident(i) if i.to_string() == "impl")); - // Ensure that we are implementing `PinnedDrop`. - let mut nesting: usize = 0; - let mut pinned_drop_idx = None; - for (i, tt) in toks.iter().enumerate() { - match tt { - TokenTree::Punct(p) if p.as_char() == '<' => { - nesting += 1; +pub(crate) fn pinned_drop(_args: Nothing, mut input: ItemImpl) -> TokenStream { + let mut errors = vec![]; + if let Some(unsafety) = input.unsafety { + errors.push(quote_spanned! {unsafety.span=> + ::core::compile_error!("implementing `PinnedDrop` is safe"); + }); + } + input.unsafety = Some(Token![unsafe](input.impl_token.span)); + match &mut input.trait_ { + Some((not, path, _for)) => { + if let Some(not) = not { + errors.push(quote_spanned! {not.span=> + ::core::compile_error!("cannot implement `!PinnedDrop`"); + }); } - TokenTree::Punct(p) if p.as_char() == '>' => { - nesting = nesting.checked_sub(1).unwrap(); - continue; + for (seg, expected) in path + .segments + .iter() + .rev() + .zip(["PinnedDrop", "pin_init", ""]) + { + if expected.is_empty() || seg.ident != expected { + errors.push(quote_spanned! {seg.span()=> + ::core::compile_error!("bad import path for `PinnedDrop`"); + }); + } + if !seg.arguments.is_none() { + errors.push(quote_spanned! {seg.arguments.span()=> + ::core::compile_error!("unexpected arguments for `PinnedDrop` path"); + }); + } } - _ => {} - } - if i >= 1 && nesting == 0 { - // Found the end of the generics, this should be `PinnedDrop`. - assert!( - matches!(tt, TokenTree::Ident(i) if i.to_string() == "PinnedDrop"), - "expected 'PinnedDrop', found: '{tt:?}'" - ); - pinned_drop_idx = Some(i); - break; + *path = parse_quote!(::pin_init::PinnedDrop); } + None => errors.push(quote_spanned! {input.impl_token.span=> + ::core::compile_error!("expected `impl ... PinnedDrop for ...`, got inherent impl"); + }), } - let idx = pinned_drop_idx - .unwrap_or_else(|| panic!("Expected an `impl` block implementing `PinnedDrop`.")); - // Fully qualify the `PinnedDrop`, as to avoid any tampering. - toks.splice(idx..idx, quote!(::pin_init::)); - // Take the `{}` body and call the declarative macro. - if let Some(TokenTree::Group(last)) = toks.pop() { - let last = last.stream(); - quote!(::pin_init::__pinned_drop! { - @impl_sig(#(#toks)*), - @impl_body(#last), - }) - } else { - TokenStream::from_iter(toks) + for item in &mut input.items { + if let ImplItem::Fn(fn_item) = item { + if fn_item.sig.ident == "drop" { + fn_item + .sig + .inputs + .push(parse_quote!(_: ::pin_init::__internal::OnlyCallFromDrop)); + } + } } + quote!(#(#errors)* #input) } diff --git a/internal/src/zeroable.rs b/internal/src/zeroable.rs index e0ed3998..160fb954 100644 --- a/internal/src/zeroable.rs +++ b/internal/src/zeroable.rs @@ -1,101 +1,71 @@ // SPDX-License-Identifier: GPL-2.0 -#[cfg(not(kernel))] -use proc_macro2 as proc_macro; +use proc_macro2::TokenStream; +use quote::{quote, quote_spanned}; +use syn::{parse_quote, Data, DeriveInput, Field, Fields}; -use crate::helpers::{parse_generics, Generics}; -use proc_macro::{TokenStream, TokenTree}; - -pub(crate) fn parse_zeroable_derive_input( - input: TokenStream, -) -> ( - Vec, - Vec, - Vec, - Option, -) { - let ( - Generics { - impl_generics, - decl_generics: _, - ty_generics, - }, - mut rest, - ) = parse_generics(input); - // This should be the body of the struct `{...}`. - let last = rest.pop(); - // Now we insert `Zeroable` as a bound for every generic parameter in `impl_generics`. - let mut new_impl_generics = Vec::with_capacity(impl_generics.len()); - // Are we inside of a generic where we want to add `Zeroable`? - let mut in_generic = !impl_generics.is_empty(); - // Have we already inserted `Zeroable`? - let mut inserted = false; - // Level of `<>` nestings. - let mut nested = 0; - for tt in impl_generics { - match &tt { - // If we find a `,`, then we have finished a generic/constant/lifetime parameter. - TokenTree::Punct(p) if nested == 0 && p.as_char() == ',' => { - if in_generic && !inserted { - new_impl_generics.extend(quote! { : ::pin_init::Zeroable }); - } - in_generic = true; - inserted = false; - new_impl_generics.push(tt); - } - // If we find `'`, then we are entering a lifetime. - TokenTree::Punct(p) if nested == 0 && p.as_char() == '\'' => { - in_generic = false; - new_impl_generics.push(tt); - } - TokenTree::Punct(p) if nested == 0 && p.as_char() == ':' => { - new_impl_generics.push(tt); - if in_generic { - new_impl_generics.extend(quote! { ::pin_init::Zeroable + }); - inserted = true; - } - } - TokenTree::Punct(p) if p.as_char() == '<' => { - nested += 1; - new_impl_generics.push(tt); - } - TokenTree::Punct(p) if p.as_char() == '>' => { - assert!(nested > 0); - nested -= 1; - new_impl_generics.push(tt); - } - _ => new_impl_generics.push(tt), +pub(crate) fn derive(input: DeriveInput) -> TokenStream { + let fields = match input.data { + Data::Struct(data_struct) => data_struct.fields, + Data::Union(data_union) => Fields::Named(data_union.fields), + Data::Enum(data_enum) => { + return quote_spanned! {data_enum.enum_token.span=> + ::core::compile_error!("cannot derive `Zeroable` for an enum"); + }; } + }; + let name = input.ident; + let mut generics = input.generics; + for param in generics.type_params_mut() { + param.bounds.push(parse_quote!(::pin_init::Zeroable)); } - assert_eq!(nested, 0); - if in_generic && !inserted { - new_impl_generics.extend(quote! { : ::pin_init::Zeroable }); - } - (rest, new_impl_generics, ty_generics, last) -} - -pub(crate) fn derive(input: TokenStream) -> TokenStream { - let (rest, new_impl_generics, ty_generics, last) = parse_zeroable_derive_input(input); + let (impl_gen, ty_gen, whr) = generics.split_for_impl(); + let field_type = fields.iter().map(|field| &field.ty); quote! { - ::pin_init::__derive_zeroable!( - parse_input: - @sig(#(#rest)*), - @impl_generics(#(#new_impl_generics)*), - @ty_generics(#(#ty_generics)*), - @body(#last), - ); + // SAFETY: Every field type implements `Zeroable` and padding bytes may be zero. + #[automatically_derived] + unsafe impl #impl_gen ::pin_init::Zeroable for #name #ty_gen + #whr + {} + const _: () = { + fn assert_zeroable() {} + fn ensure_zeroable #impl_gen () + #whr + { + #( + assert_zeroable::<#field_type>(); + )* + } + }; } } -pub(crate) fn maybe_derive(input: TokenStream) -> TokenStream { - let (rest, new_impl_generics, ty_generics, last) = parse_zeroable_derive_input(input); +pub(crate) fn maybe_derive(input: DeriveInput) -> TokenStream { + let fields = match input.data { + Data::Struct(data_struct) => data_struct.fields, + Data::Union(data_union) => Fields::Named(data_union.fields), + Data::Enum(data_enum) => { + return quote_spanned! {data_enum.enum_token.span=> + compile_error!("cannot derive `Zeroable` for an enum"); + }; + } + }; + let name = input.ident; + let mut generics = input.generics; + for Field { ty, .. } in fields { + generics + .make_where_clause() + .predicates + // the `for<'__dummy>` HRTB makes this not error without the `trivial_bounds` + // feature . + .push(parse_quote!(#ty: for<'__dummy> ::pin_init::Zeroable)); + } + let (impl_gen, ty_gen, whr) = generics.split_for_impl(); quote! { - ::pin_init::__maybe_derive_zeroable!( - parse_input: - @sig(#(#rest)*), - @impl_generics(#(#new_impl_generics)*), - @ty_generics(#(#ty_generics)*), - @body(#last), - ); + // SAFETY: Every field type implements `Zeroable` and padding bytes may be zero. + #[automatically_derived] + unsafe impl #impl_gen ::pin_init::Zeroable for #name #ty_gen + #whr + {} } } diff --git a/src/lib.rs b/src/lib.rs index dd553212..43868fa1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -146,7 +146,7 @@ //! //! impl DriverData { //! fn new() -> impl PinInit { -//! try_pin_init!(Self { +//! pin_init!(Self { //! status <- CMutex::new(0), //! buffer: Box::init(pin_init::init_zeroed())?, //! }? Error) @@ -290,10 +290,13 @@ use core::{ ptr::{self, NonNull}, }; +// This is used by doc-tests -- the proc-macros expand to `::pin_init::...` and without this the +// doc-tests wouldn't have an extern crate named `pin_init`. +#[allow(unused_extern_crates)] +extern crate self as pin_init; + #[doc(hidden)] pub mod __internal; -#[doc(hidden)] -pub mod macros; #[cfg(any(feature = "std", feature = "alloc"))] mod alloc; @@ -528,7 +531,7 @@ macro_rules! stack_pin_init { /// x: u32, /// } /// -/// stack_try_pin_init!(let foo: Foo = try_pin_init!(Foo { +/// stack_try_pin_init!(let foo: Foo = pin_init!(Foo { /// a <- CMutex::new(42), /// b: Box::try_new(Bar { /// x: 64, @@ -555,7 +558,7 @@ macro_rules! stack_pin_init { /// x: u32, /// } /// -/// stack_try_pin_init!(let foo: Foo =? try_pin_init!(Foo { +/// stack_try_pin_init!(let foo: Foo =? pin_init!(Foo { /// a <- CMutex::new(42), /// b: Box::try_new(Bar { /// x: 64, @@ -584,10 +587,10 @@ macro_rules! stack_try_pin_init { }; } -/// Construct an in-place, pinned initializer for `struct`s. +/// Construct an in-place, fallible pinned initializer for `struct`s. /// -/// This macro defaults the error to [`Infallible`]. If you need a different error, then use -/// [`try_pin_init!`]. +/// The error type defaults to [`Infallible`]; if you need a different one, write `? Error` at the +/// end, after the struct initializer. /// /// The syntax is almost identical to that of a normal `struct` initializer: /// @@ -776,81 +779,12 @@ macro_rules! stack_try_pin_init { /// ``` /// /// [`NonNull`]: core::ptr::NonNull -// For a detailed example of how this macro works, see the module documentation of the hidden -// module `macros` inside of `macros.rs`. -#[macro_export] -macro_rules! pin_init { - ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { - $($fields:tt)* - }) => { - $crate::try_pin_init!($(&$this in)? $t $(::<$($generics),*>)? { - $($fields)* - }? ::core::convert::Infallible) - }; -} +pub use pin_init_internal::pin_init; -/// Construct an in-place, fallible pinned initializer for `struct`s. -/// -/// If the initialization can complete without error (or [`Infallible`]), then use [`pin_init!`]. +/// Construct an in-place, fallible initializer for `struct`s. /// -/// You can use the `?` operator or use `return Err(err)` inside the initializer to stop -/// initialization and return the error. -/// -/// IMPORTANT: if you have `unsafe` code inside of the initializer you have to ensure that when -/// initialization fails, the memory can be safely deallocated without any further modifications. -/// -/// The syntax is identical to [`pin_init!`] with the following exception: you must append `? $type` -/// after the `struct` initializer to specify the error type you want to use. -/// -/// # Examples -/// -/// ```rust -/// # #![feature(allocator_api)] -/// # #[path = "../examples/error.rs"] mod error; use error::Error; -/// use pin_init::{pin_data, try_pin_init, PinInit, InPlaceInit, init_zeroed}; -/// -/// #[pin_data] -/// struct BigBuf { -/// big: Box<[u8; 1024 * 1024 * 1024]>, -/// small: [u8; 1024 * 1024], -/// ptr: *mut u8, -/// } -/// -/// impl BigBuf { -/// fn new() -> impl PinInit { -/// try_pin_init!(Self { -/// big: Box::init(init_zeroed())?, -/// small: [0; 1024 * 1024], -/// ptr: core::ptr::null_mut(), -/// }? Error) -/// } -/// } -/// # let _ = Box::pin_init(BigBuf::new()); -/// ``` -// For a detailed example of how this macro works, see the module documentation of the hidden -// module `macros` inside of `macros.rs`. -#[macro_export] -macro_rules! try_pin_init { - ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { - $($fields:tt)* - }? $err:ty) => { - $crate::__init_internal!( - @this($($this)?), - @typ($t $(::<$($generics),*>)? ), - @fields($($fields)*), - @error($err), - @data(PinData, use_data), - @has_data(HasPinData, __pin_data), - @construct_closure(pin_init_from_closure), - @munch_fields($($fields)*), - ) - } -} - -/// Construct an in-place initializer for `struct`s. -/// -/// This macro defaults the error to [`Infallible`]. If you need a different error, then use -/// [`try_init!`]. +/// This macro defaults the error to [`Infallible`]; if you need a different one, write `? Error` +/// at the end, after the struct initializer. /// /// The syntax is identical to [`pin_init!`] and its safety caveats also apply: /// - `unsafe` code must guarantee either full initialization or return an error and allow @@ -883,74 +817,7 @@ macro_rules! try_pin_init { /// } /// # let _ = Box::init(BigBuf::new()); /// ``` -// For a detailed example of how this macro works, see the module documentation of the hidden -// module `macros` inside of `macros.rs`. -#[macro_export] -macro_rules! init { - ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { - $($fields:tt)* - }) => { - $crate::try_init!($(&$this in)? $t $(::<$($generics),*>)? { - $($fields)* - }? ::core::convert::Infallible) - } -} - -/// Construct an in-place fallible initializer for `struct`s. -/// -/// If the initialization can complete without error (or [`Infallible`]), then use -/// [`init!`]. -/// -/// The syntax is identical to [`try_pin_init!`]. You need to specify a custom error -/// via `? $type` after the `struct` initializer. -/// The safety caveats from [`try_pin_init!`] also apply: -/// - `unsafe` code must guarantee either full initialization or return an error and allow -/// deallocation of the memory. -/// - the fields are initialized in the order given in the initializer. -/// - no references to fields are allowed to be created inside of the initializer. -/// -/// # Examples -/// -/// ```rust -/// # #![feature(allocator_api)] -/// # use core::alloc::AllocError; -/// # use pin_init::InPlaceInit; -/// use pin_init::{try_init, Init, init_zeroed}; -/// -/// struct BigBuf { -/// big: Box<[u8; 1024 * 1024 * 1024]>, -/// small: [u8; 1024 * 1024], -/// } -/// -/// impl BigBuf { -/// fn new() -> impl Init { -/// try_init!(Self { -/// big: Box::init(init_zeroed())?, -/// small: [0; 1024 * 1024], -/// }? AllocError) -/// } -/// } -/// # let _ = Box::init(BigBuf::new()); -/// ``` -// For a detailed example of how this macro works, see the module documentation of the hidden -// module `macros` inside of `macros.rs`. -#[macro_export] -macro_rules! try_init { - ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { - $($fields:tt)* - }? $err:ty) => { - $crate::__init_internal!( - @this($($this)?), - @typ($t $(::<$($generics),*>)?), - @fields($($fields)*), - @error($err), - @data(InitData, /*no use_data*/), - @has_data(HasInitData, __init_data), - @construct_closure(init_from_closure), - @munch_fields($($fields)*), - ) - } -} +pub use pin_init_internal::init; /// Asserts that a field on a struct using `#[pin_data]` is marked with `#[pin]` ie. that it is /// structurally pinned. diff --git a/src/macros.rs b/src/macros.rs deleted file mode 100644 index 682c61a5..00000000 --- a/src/macros.rs +++ /dev/null @@ -1,1677 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 OR MIT - -//! This module provides the macros that actually implement the proc-macros `pin_data` and -//! `pinned_drop`. It also contains `__init_internal`, the implementation of the -//! `{try_}{pin_}init!` macros. -//! -//! These macros should never be called directly, since they expect their input to be -//! in a certain format which is internal. If used incorrectly, these macros can lead to UB even in -//! safe code! Use the public facing macros instead. -//! -//! This architecture has been chosen because the kernel does not yet have access to `syn` which -//! would make matters a lot easier for implementing these as proc-macros. -//! -//! Since this library and the kernel implementation should diverge as little as possible, the same -//! approach has been taken here. -//! -//! # Macro expansion example -//! -//! This section is intended for readers trying to understand the macros in this module and the -//! `[try_][pin_]init!` macros from `lib.rs`. -//! -//! We will look at the following example: -//! -//! ```rust,ignore -//! #[pin_data] -//! #[repr(C)] -//! struct Bar { -//! #[pin] -//! t: T, -//! pub x: usize, -//! } -//! -//! impl Bar { -//! fn new(t: T) -> impl PinInit { -//! pin_init!(Self { t, x: 0 }) -//! } -//! } -//! -//! #[pin_data(PinnedDrop)] -//! struct Foo { -//! a: usize, -//! #[pin] -//! b: Bar, -//! } -//! -//! #[pinned_drop] -//! impl PinnedDrop for Foo { -//! fn drop(self: Pin<&mut Self>) { -//! println!("{self:p} is getting dropped."); -//! } -//! } -//! -//! let a = 42; -//! let initializer = pin_init!(Foo { -//! a, -//! b <- Bar::new(36), -//! }); -//! ``` -//! -//! This example includes the most common and important features of the pin-init API. -//! -//! Below you can find individual section about the different macro invocations. Here are some -//! general things we need to take into account when designing macros: -//! - use global paths, similarly to file paths, these start with the separator: `::core::panic!()` -//! this ensures that the correct item is used, since users could define their own `mod core {}` -//! and then their own `panic!` inside to execute arbitrary code inside of our macro. -//! - macro `unsafe` hygiene: we need to ensure that we do not expand arbitrary, user-supplied -//! expressions inside of an `unsafe` block in the macro, because this would allow users to do -//! `unsafe` operations without an associated `unsafe` block. -//! -//! ## `#[pin_data]` on `Bar` -//! -//! This macro is used to specify which fields are structurally pinned and which fields are not. It -//! is placed on the struct definition and allows `#[pin]` to be placed on the fields. -//! -//! Here is the definition of `Bar` from our example: -//! -//! ```rust,ignore -//! #[pin_data] -//! #[repr(C)] -//! struct Bar { -//! #[pin] -//! t: T, -//! pub x: usize, -//! } -//! ``` -//! -//! This expands to the following code: -//! -//! ```rust,ignore -//! // Firstly the normal definition of the struct, attributes are preserved: -//! #[repr(C)] -//! struct Bar { -//! t: T, -//! pub x: usize, -//! } -//! // Then an anonymous constant is defined, this is because we do not want any code to access the -//! // types that we define inside: -//! const _: () = { -//! // We define the pin-data carrying struct, it is a ZST and needs to have the same generics, -//! // since we need to implement access functions for each field and thus need to know its -//! // type. -//! struct __ThePinData { -//! __phantom: ::core::marker::PhantomData) -> Bar>, -//! } -//! // We implement `Copy` for the pin-data struct, since all functions it defines will take -//! // `self` by value. -//! impl ::core::clone::Clone for __ThePinData { -//! fn clone(&self) -> Self { -//! *self -//! } -//! } -//! impl ::core::marker::Copy for __ThePinData {} -//! // For every field of `Bar`, the pin-data struct will define a function with the same name -//! // and accessor (`pub` or `pub(crate)` etc.). This function will take a pointer to the -//! // field (`slot`) and a `PinInit` or `Init` depending on the projection kind of the field -//! // (if pinning is structural for the field, then `PinInit` otherwise `Init`). -//! #[allow(dead_code)] -//! impl __ThePinData { -//! unsafe fn t( -//! self, -//! slot: *mut T, -//! // Since `t` is `#[pin]`, this is `PinInit`. -//! init: impl ::pin_init::PinInit, -//! ) -> ::core::result::Result<(), E> { -//! unsafe { ::pin_init::PinInit::__pinned_init(init, slot) } -//! } -//! pub unsafe fn x( -//! self, -//! slot: *mut usize, -//! // Since `x` is not `#[pin]`, this is `Init`. -//! init: impl ::pin_init::Init, -//! ) -> ::core::result::Result<(), E> { -//! unsafe { ::pin_init::Init::__init(init, slot) } -//! } -//! } -//! // Implement the internal `HasPinData` trait that associates `Bar` with the pin-data struct -//! // that we constructed above. -//! unsafe impl ::pin_init::__internal::HasPinData for Bar { -//! type PinData = __ThePinData; -//! unsafe fn __pin_data() -> Self::PinData { -//! __ThePinData { -//! __phantom: ::core::marker::PhantomData, -//! } -//! } -//! } -//! // Implement the internal `PinData` trait that marks the pin-data struct as a pin-data -//! // struct. This is important to ensure that no user can implement a rogue `__pin_data` -//! // function without using `unsafe`. -//! unsafe impl ::pin_init::__internal::PinData for __ThePinData { -//! type Datee = Bar; -//! } -//! // Now we only want to implement `Unpin` for `Bar` when every structurally pinned field is -//! // `Unpin`. In other words, whether `Bar` is `Unpin` only depends on structurally pinned -//! // fields (those marked with `#[pin]`). These fields will be listed in this struct, in our -//! // case no such fields exist, hence this is almost empty. The two phantomdata fields exist -//! // for two reasons: -//! // - `__phantom`: every generic must be used, since we cannot really know which generics -//! // are used, we declare all and then use everything here once. -//! // - `__phantom_pin`: uses the `'__pin` lifetime and ensures that this struct is invariant -//! // over it. The lifetime is needed to work around the limitation that trait bounds must -//! // not be trivial, e.g. the user has a `#[pin] PhantomPinned` field -- this is -//! // unconditionally `!Unpin` and results in an error. The lifetime tricks the compiler -//! // into accepting these bounds regardless. -//! #[allow(dead_code)] -//! struct __Unpin<'__pin, T> { -//! __phantom_pin: ::core::marker::PhantomData &'__pin ()>, -//! __phantom: ::core::marker::PhantomData) -> Bar>, -//! // Our only `#[pin]` field is `t`. -//! t: T, -//! } -//! #[doc(hidden)] -//! impl<'__pin, T> ::core::marker::Unpin for Bar -//! where -//! __Unpin<'__pin, T>: ::core::marker::Unpin, -//! {} -//! // Now we need to ensure that `Bar` does not implement `Drop`, since that would give users -//! // access to `&mut self` inside of `drop` even if the struct was pinned. This could lead to -//! // UB with only safe code, so we disallow this by giving a trait implementation error using -//! // a direct impl and a blanket implementation. -//! trait MustNotImplDrop {} -//! // Normally `Drop` bounds do not have the correct semantics, but for this purpose they do -//! // (normally people want to know if a type has any kind of drop glue at all, here we want -//! // to know if it has any kind of custom drop glue, which is exactly what this bound does). -//! #[expect(drop_bounds)] -//! impl MustNotImplDrop for T {} -//! impl MustNotImplDrop for Bar {} -//! // Here comes a convenience check, if one implemented `PinnedDrop`, but forgot to add it to -//! // `#[pin_data]`, then this will error with the same mechanic as above, this is not needed -//! // for safety, but a good sanity check, since no normal code calls `PinnedDrop::drop`. -//! #[expect(non_camel_case_types)] -//! trait UselessPinnedDropImpl_you_need_to_specify_PinnedDrop {} -//! impl< -//! T: ::pin_init::PinnedDrop, -//! > UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for T {} -//! impl UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for Bar {} -//! }; -//! ``` -//! -//! ## `pin_init!` in `impl Bar` -//! -//! This macro creates an pin-initializer for the given struct. It requires that the struct is -//! annotated by `#[pin_data]`. -//! -//! Here is the impl on `Bar` defining the new function: -//! -//! ```rust,ignore -//! impl Bar { -//! fn new(t: T) -> impl PinInit { -//! pin_init!(Self { t, x: 0 }) -//! } -//! } -//! ``` -//! -//! This expands to the following code: -//! -//! ```rust,ignore -//! impl Bar { -//! fn new(t: T) -> impl PinInit { -//! { -//! // We do not want to allow arbitrary returns, so we declare this type as the `Ok` -//! // return type and shadow it later when we insert the arbitrary user code. That way -//! // there will be no possibility of returning without `unsafe`. -//! struct __InitOk; -//! // Get the data about fields from the supplied type. -//! // - the function is unsafe, hence the unsafe block -//! // - we `use` the `HasPinData` trait in the block, it is only available in that -//! // scope. -//! let data = unsafe { -//! use ::pin_init::__internal::HasPinData; -//! Self::__pin_data() -//! }; -//! // Ensure that `data` really is of type `PinData` and help with type inference: -//! let init = ::pin_init::__internal::PinData::make_closure::< -//! _, -//! __InitOk, -//! ::core::convert::Infallible, -//! >(data, move |slot| { -//! { -//! // Shadow the structure so it cannot be used to return early. If a user -//! // tries to write `return Ok(__InitOk)`, then they get a type error, -//! // since that will refer to this struct instead of the one defined -//! // above. -//! struct __InitOk; -//! // This is the expansion of `t,`, which is syntactic sugar for `t: t,`. -//! { -//! unsafe { ::core::ptr::write(::core::addr_of_mut!((*slot).t), t) }; -//! } -//! // Since initialization could fail later (not in this case, since the -//! // error type is `Infallible`) we will need to drop this field if there -//! // is an error later. This `DropGuard` will drop the field when it gets -//! // dropped and has not yet been forgotten. -//! let __t_guard = unsafe { -//! ::pin_init::__internal::DropGuard::new(::core::addr_of_mut!((*slot).t)) -//! }; -//! // Expansion of `x: 0,`: -//! // Since this can be an arbitrary expression we cannot place it inside -//! // of the `unsafe` block, so we bind it here. -//! { -//! let x = 0; -//! unsafe { ::core::ptr::write(::core::addr_of_mut!((*slot).x), x) }; -//! } -//! // We again create a `DropGuard`. -//! let __x_guard = unsafe { -//! ::pin_init::__internal::DropGuard::new(::core::addr_of_mut!((*slot).x)) -//! }; -//! // Since initialization has successfully completed, we can now forget -//! // the guards. This is not `mem::forget`, since we only have -//! // `&DropGuard`. -//! ::core::mem::forget(__x_guard); -//! ::core::mem::forget(__t_guard); -//! // Here we use the type checker to ensure that every field has been -//! // initialized exactly once, since this is `if false` it will never get -//! // executed, but still type-checked. -//! // Additionally we abuse `slot` to automatically infer the correct type -//! // for the struct. This is also another check that every field is -//! // accessible from this scope. -//! #[allow(unreachable_code, clippy::diverging_sub_expression)] -//! let _ = || { -//! unsafe { -//! ::core::ptr::write( -//! slot, -//! Self { -//! // We only care about typecheck finding every field -//! // here, the expression does not matter, just conjure -//! // one using `panic!()`: -//! t: ::core::panic!(), -//! x: ::core::panic!(), -//! }, -//! ); -//! }; -//! }; -//! } -//! // We leave the scope above and gain access to the previously shadowed -//! // `__InitOk` that we need to return. -//! Ok(__InitOk) -//! }); -//! // Change the return type from `__InitOk` to `()`. -//! let init = move | -//! slot, -//! | -> ::core::result::Result<(), ::core::convert::Infallible> { -//! init(slot).map(|__InitOk| ()) -//! }; -//! // Construct the initializer. -//! let init = unsafe { -//! ::pin_init::pin_init_from_closure::< -//! _, -//! ::core::convert::Infallible, -//! >(init) -//! }; -//! init -//! } -//! } -//! } -//! ``` -//! -//! ## `#[pin_data]` on `Foo` -//! -//! Since we already took a look at `#[pin_data]` on `Bar`, this section will only explain the -//! differences/new things in the expansion of the `Foo` definition: -//! -//! ```rust,ignore -//! #[pin_data(PinnedDrop)] -//! struct Foo { -//! a: usize, -//! #[pin] -//! b: Bar, -//! } -//! ``` -//! -//! This expands to the following code: -//! -//! ```rust,ignore -//! struct Foo { -//! a: usize, -//! b: Bar, -//! } -//! const _: () = { -//! struct __ThePinData { -//! __phantom: ::core::marker::PhantomData Foo>, -//! } -//! impl ::core::clone::Clone for __ThePinData { -//! fn clone(&self) -> Self { -//! *self -//! } -//! } -//! impl ::core::marker::Copy for __ThePinData {} -//! #[allow(dead_code)] -//! impl __ThePinData { -//! unsafe fn b( -//! self, -//! slot: *mut Bar, -//! init: impl ::pin_init::PinInit, E>, -//! ) -> ::core::result::Result<(), E> { -//! unsafe { ::pin_init::PinInit::__pinned_init(init, slot) } -//! } -//! unsafe fn a( -//! self, -//! slot: *mut usize, -//! init: impl ::pin_init::Init, -//! ) -> ::core::result::Result<(), E> { -//! unsafe { ::pin_init::Init::__init(init, slot) } -//! } -//! } -//! unsafe impl ::pin_init::__internal::HasPinData for Foo { -//! type PinData = __ThePinData; -//! unsafe fn __pin_data() -> Self::PinData { -//! __ThePinData { -//! __phantom: ::core::marker::PhantomData, -//! } -//! } -//! } -//! unsafe impl ::pin_init::__internal::PinData for __ThePinData { -//! type Datee = Foo; -//! } -//! #[allow(dead_code)] -//! struct __Unpin<'__pin> { -//! __phantom_pin: ::core::marker::PhantomData &'__pin ()>, -//! __phantom: ::core::marker::PhantomData Foo>, -//! b: Bar, -//! } -//! #[doc(hidden)] -//! impl<'__pin> ::core::marker::Unpin for Foo -//! where -//! __Unpin<'__pin>: ::core::marker::Unpin, -//! {} -//! // Since we specified `PinnedDrop` as the argument to `#[pin_data]`, we expect `Foo` to -//! // implement `PinnedDrop`. Thus we do not need to prevent `Drop` implementations like -//! // before, instead we implement `Drop` here and delegate to `PinnedDrop`. -//! impl ::core::ops::Drop for Foo { -//! fn drop(&mut self) { -//! // Since we are getting dropped, no one else has a reference to `self` and thus we -//! // can assume that we never move. -//! let pinned = unsafe { ::core::pin::Pin::new_unchecked(self) }; -//! // Create the unsafe token that proves that we are inside of a destructor, this -//! // type is only allowed to be created in a destructor. -//! let token = unsafe { ::pin_init::__internal::OnlyCallFromDrop::new() }; -//! ::pin_init::PinnedDrop::drop(pinned, token); -//! } -//! } -//! }; -//! ``` -//! -//! ## `#[pinned_drop]` on `impl PinnedDrop for Foo` -//! -//! This macro is used to implement the `PinnedDrop` trait, since that trait is `unsafe` and has an -//! extra parameter that should not be used at all. The macro hides that parameter. -//! -//! Here is the `PinnedDrop` impl for `Foo`: -//! -//! ```rust,ignore -//! #[pinned_drop] -//! impl PinnedDrop for Foo { -//! fn drop(self: Pin<&mut Self>) { -//! println!("{self:p} is getting dropped."); -//! } -//! } -//! ``` -//! -//! This expands to the following code: -//! -//! ```rust,ignore -//! // `unsafe`, full path and the token parameter are added, everything else stays the same. -//! unsafe impl ::pin_init::PinnedDrop for Foo { -//! fn drop(self: Pin<&mut Self>, _: ::pin_init::__internal::OnlyCallFromDrop) { -//! println!("{self:p} is getting dropped."); -//! } -//! } -//! ``` -//! -//! ## `pin_init!` on `Foo` -//! -//! Since we already took a look at `pin_init!` on `Bar`, this section will only show the expansion -//! of `pin_init!` on `Foo`: -//! -//! ```rust,ignore -//! let a = 42; -//! let initializer = pin_init!(Foo { -//! a, -//! b <- Bar::new(36), -//! }); -//! ``` -//! -//! This expands to the following code: -//! -//! ```rust,ignore -//! let a = 42; -//! let initializer = { -//! struct __InitOk; -//! let data = unsafe { -//! use ::pin_init::__internal::HasPinData; -//! Foo::__pin_data() -//! }; -//! let init = ::pin_init::__internal::PinData::make_closure::< -//! _, -//! __InitOk, -//! ::core::convert::Infallible, -//! >(data, move |slot| { -//! { -//! struct __InitOk; -//! { -//! unsafe { ::core::ptr::write(::core::addr_of_mut!((*slot).a), a) }; -//! } -//! let __a_guard = unsafe { -//! ::pin_init::__internal::DropGuard::new(::core::addr_of_mut!((*slot).a)) -//! }; -//! let init = Bar::new(36); -//! unsafe { data.b(::core::addr_of_mut!((*slot).b), b)? }; -//! let __b_guard = unsafe { -//! ::pin_init::__internal::DropGuard::new(::core::addr_of_mut!((*slot).b)) -//! }; -//! ::core::mem::forget(__b_guard); -//! ::core::mem::forget(__a_guard); -//! #[allow(unreachable_code, clippy::diverging_sub_expression)] -//! let _ = || { -//! unsafe { -//! ::core::ptr::write( -//! slot, -//! Foo { -//! a: ::core::panic!(), -//! b: ::core::panic!(), -//! }, -//! ); -//! }; -//! }; -//! } -//! Ok(__InitOk) -//! }); -//! let init = move | -//! slot, -//! | -> ::core::result::Result<(), ::core::convert::Infallible> { -//! init(slot).map(|__InitOk| ()) -//! }; -//! let init = unsafe { -//! ::pin_init::pin_init_from_closure::<_, ::core::convert::Infallible>(init) -//! }; -//! init -//! }; -//! ``` - -#[cfg(kernel)] -pub use ::macros::paste; -#[cfg(not(kernel))] -pub use ::paste::paste; - -/// Creates a `unsafe impl<...> PinnedDrop for $type` block. -/// -/// See [`PinnedDrop`] for more information. -/// -/// [`PinnedDrop`]: crate::PinnedDrop -#[doc(hidden)] -#[macro_export] -macro_rules! __pinned_drop { - ( - @impl_sig($($impl_sig:tt)*), - @impl_body( - $(#[$($attr:tt)*])* - fn drop($($sig:tt)*) { - $($inner:tt)* - } - ), - ) => { - // SAFETY: TODO. - unsafe $($impl_sig)* { - // Inherit all attributes and the type/ident tokens for the signature. - $(#[$($attr)*])* - fn drop($($sig)*, _: $crate::__internal::OnlyCallFromDrop) { - $($inner)* - } - } - } -} - -/// This macro first parses the struct definition such that it separates pinned and not pinned -/// fields. Afterwards it declares the struct and implement the `PinData` trait safely. -#[doc(hidden)] -#[macro_export] -macro_rules! __pin_data { - // Proc-macro entry point, this is supplied by the proc-macro pre-parsing. - (parse_input: - @args($($pinned_drop:ident)?), - @sig( - $(#[$($struct_attr:tt)*])* - $vis:vis struct $name:ident - $(where $($whr:tt)*)? - ), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @decl_generics($($decl_generics:tt)*), - @body({ $($fields:tt)* }), - ) => { - // We now use token munching to iterate through all of the fields. While doing this we - // identify fields marked with `#[pin]`, these fields are the 'pinned fields'. The user - // wants these to be structurally pinned. The rest of the fields are the - // 'not pinned fields'. Additionally we collect all fields, since we need them in the right - // order to declare the struct. - // - // In this call we also put some explaining comments for the parameters. - $crate::__pin_data!(find_pinned_fields: - // Attributes on the struct itself, these will just be propagated to be put onto the - // struct definition. - @struct_attrs($(#[$($struct_attr)*])*), - // The visibility of the struct. - @vis($vis), - // The name of the struct. - @name($name), - // The 'impl generics', the generics that will need to be specified on the struct inside - // of an `impl<$ty_generics>` block. - @impl_generics($($impl_generics)*), - // The 'ty generics', the generics that will need to be specified on the impl blocks. - @ty_generics($($ty_generics)*), - // The 'decl generics', the generics that need to be specified on the struct - // definition. - @decl_generics($($decl_generics)*), - // The where clause of any impl block and the declaration. - @where($($($whr)*)?), - // The remaining fields tokens that need to be processed. - // We add a `,` at the end to ensure correct parsing. - @fields_munch($($fields)* ,), - // The pinned fields. - @pinned(), - // The not pinned fields. - @not_pinned(), - // All fields. - @fields(), - // The accumulator containing all attributes already parsed. - @accum(), - // Contains `yes` or `` to indicate if `#[pin]` was found on the current field. - @is_pinned(), - // The proc-macro argument, this should be `PinnedDrop` or ``. - @pinned_drop($($pinned_drop)?), - ); - }; - (find_pinned_fields: - @struct_attrs($($struct_attrs:tt)*), - @vis($vis:vis), - @name($name:ident), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @decl_generics($($decl_generics:tt)*), - @where($($whr:tt)*), - // We found a PhantomPinned field, this should generally be pinned! - @fields_munch($field:ident : $($($(::)?core::)?marker::)?PhantomPinned, $($rest:tt)*), - @pinned($($pinned:tt)*), - @not_pinned($($not_pinned:tt)*), - @fields($($fields:tt)*), - @accum($($accum:tt)*), - // This field is not pinned. - @is_pinned(), - @pinned_drop($($pinned_drop:ident)?), - ) => { - ::core::compile_error!(concat!( - "The field `", - stringify!($field), - "` of type `PhantomPinned` only has an effect, if it has the `#[pin]` attribute.", - )); - $crate::__pin_data!(find_pinned_fields: - @struct_attrs($($struct_attrs)*), - @vis($vis), - @name($name), - @impl_generics($($impl_generics)*), - @ty_generics($($ty_generics)*), - @decl_generics($($decl_generics)*), - @where($($whr)*), - @fields_munch($($rest)*), - @pinned($($pinned)* $($accum)* $field: ::core::marker::PhantomPinned,), - @not_pinned($($not_pinned)*), - @fields($($fields)* $($accum)* $field: ::core::marker::PhantomPinned,), - @accum(), - @is_pinned(), - @pinned_drop($($pinned_drop)?), - ); - }; - (find_pinned_fields: - @struct_attrs($($struct_attrs:tt)*), - @vis($vis:vis), - @name($name:ident), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @decl_generics($($decl_generics:tt)*), - @where($($whr:tt)*), - // We reached the field declaration. - @fields_munch($field:ident : $type:ty, $($rest:tt)*), - @pinned($($pinned:tt)*), - @not_pinned($($not_pinned:tt)*), - @fields($($fields:tt)*), - @accum($($accum:tt)*), - // This field is pinned. - @is_pinned(yes), - @pinned_drop($($pinned_drop:ident)?), - ) => { - $crate::__pin_data!(find_pinned_fields: - @struct_attrs($($struct_attrs)*), - @vis($vis), - @name($name), - @impl_generics($($impl_generics)*), - @ty_generics($($ty_generics)*), - @decl_generics($($decl_generics)*), - @where($($whr)*), - @fields_munch($($rest)*), - @pinned($($pinned)* $($accum)* $field: $type,), - @not_pinned($($not_pinned)*), - @fields($($fields)* $($accum)* $field: $type,), - @accum(), - @is_pinned(), - @pinned_drop($($pinned_drop)?), - ); - }; - (find_pinned_fields: - @struct_attrs($($struct_attrs:tt)*), - @vis($vis:vis), - @name($name:ident), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @decl_generics($($decl_generics:tt)*), - @where($($whr:tt)*), - // We reached the field declaration. - @fields_munch($field:ident : $type:ty, $($rest:tt)*), - @pinned($($pinned:tt)*), - @not_pinned($($not_pinned:tt)*), - @fields($($fields:tt)*), - @accum($($accum:tt)*), - // This field is not pinned. - @is_pinned(), - @pinned_drop($($pinned_drop:ident)?), - ) => { - $crate::__pin_data!(find_pinned_fields: - @struct_attrs($($struct_attrs)*), - @vis($vis), - @name($name), - @impl_generics($($impl_generics)*), - @ty_generics($($ty_generics)*), - @decl_generics($($decl_generics)*), - @where($($whr)*), - @fields_munch($($rest)*), - @pinned($($pinned)*), - @not_pinned($($not_pinned)* $($accum)* $field: $type,), - @fields($($fields)* $($accum)* $field: $type,), - @accum(), - @is_pinned(), - @pinned_drop($($pinned_drop)?), - ); - }; - (find_pinned_fields: - @struct_attrs($($struct_attrs:tt)*), - @vis($vis:vis), - @name($name:ident), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @decl_generics($($decl_generics:tt)*), - @where($($whr:tt)*), - // We found the `#[pin]` attr. - @fields_munch(#[pin] $($rest:tt)*), - @pinned($($pinned:tt)*), - @not_pinned($($not_pinned:tt)*), - @fields($($fields:tt)*), - @accum($($accum:tt)*), - @is_pinned($($is_pinned:ident)?), - @pinned_drop($($pinned_drop:ident)?), - ) => { - $crate::__pin_data!(find_pinned_fields: - @struct_attrs($($struct_attrs)*), - @vis($vis), - @name($name), - @impl_generics($($impl_generics)*), - @ty_generics($($ty_generics)*), - @decl_generics($($decl_generics)*), - @where($($whr)*), - @fields_munch($($rest)*), - // We do not include `#[pin]` in the list of attributes, since it is not actually an - // attribute that is defined somewhere. - @pinned($($pinned)*), - @not_pinned($($not_pinned)*), - @fields($($fields)*), - @accum($($accum)*), - // Set this to `yes`. - @is_pinned(yes), - @pinned_drop($($pinned_drop)?), - ); - }; - (find_pinned_fields: - @struct_attrs($($struct_attrs:tt)*), - @vis($vis:vis), - @name($name:ident), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @decl_generics($($decl_generics:tt)*), - @where($($whr:tt)*), - // We reached the field declaration with visibility, for simplicity we only munch the - // visibility and put it into `$accum`. - @fields_munch($fvis:vis $field:ident $($rest:tt)*), - @pinned($($pinned:tt)*), - @not_pinned($($not_pinned:tt)*), - @fields($($fields:tt)*), - @accum($($accum:tt)*), - @is_pinned($($is_pinned:ident)?), - @pinned_drop($($pinned_drop:ident)?), - ) => { - $crate::__pin_data!(find_pinned_fields: - @struct_attrs($($struct_attrs)*), - @vis($vis), - @name($name), - @impl_generics($($impl_generics)*), - @ty_generics($($ty_generics)*), - @decl_generics($($decl_generics)*), - @where($($whr)*), - @fields_munch($field $($rest)*), - @pinned($($pinned)*), - @not_pinned($($not_pinned)*), - @fields($($fields)*), - @accum($($accum)* $fvis), - @is_pinned($($is_pinned)?), - @pinned_drop($($pinned_drop)?), - ); - }; - (find_pinned_fields: - @struct_attrs($($struct_attrs:tt)*), - @vis($vis:vis), - @name($name:ident), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @decl_generics($($decl_generics:tt)*), - @where($($whr:tt)*), - // Some other attribute, just put it into `$accum`. - @fields_munch(#[$($attr:tt)*] $($rest:tt)*), - @pinned($($pinned:tt)*), - @not_pinned($($not_pinned:tt)*), - @fields($($fields:tt)*), - @accum($($accum:tt)*), - @is_pinned($($is_pinned:ident)?), - @pinned_drop($($pinned_drop:ident)?), - ) => { - $crate::__pin_data!(find_pinned_fields: - @struct_attrs($($struct_attrs)*), - @vis($vis), - @name($name), - @impl_generics($($impl_generics)*), - @ty_generics($($ty_generics)*), - @decl_generics($($decl_generics)*), - @where($($whr)*), - @fields_munch($($rest)*), - @pinned($($pinned)*), - @not_pinned($($not_pinned)*), - @fields($($fields)*), - @accum($($accum)* #[$($attr)*]), - @is_pinned($($is_pinned)?), - @pinned_drop($($pinned_drop)?), - ); - }; - (find_pinned_fields: - @struct_attrs($($struct_attrs:tt)*), - @vis($vis:vis), - @name($name:ident), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @decl_generics($($decl_generics:tt)*), - @where($($whr:tt)*), - // We reached the end of the fields, plus an optional additional comma, since we added one - // before and the user is also allowed to put a trailing comma. - @fields_munch($(,)?), - @pinned($($pinned:tt)*), - @not_pinned($($not_pinned:tt)*), - @fields($($fields:tt)*), - @accum(), - @is_pinned(), - @pinned_drop($($pinned_drop:ident)?), - ) => { - // Declare the struct with all fields in the correct order. - $($struct_attrs)* - $vis struct $name <$($decl_generics)*> - where $($whr)* - { - $($fields)* - } - - $crate::__pin_data!(make_pin_projections: - @vis($vis), - @name($name), - @impl_generics($($impl_generics)*), - @ty_generics($($ty_generics)*), - @decl_generics($($decl_generics)*), - @where($($whr)*), - @pinned($($pinned)*), - @not_pinned($($not_pinned)*), - ); - - // We put the rest into this const item, because it then will not be accessible to anything - // outside. - const _: () = { - // 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. - $vis struct __ThePinData<$($impl_generics)*> - where $($whr)* - { - __phantom: ::core::marker::PhantomData< - fn($name<$($ty_generics)*>) -> $name<$($ty_generics)*> - >, - } - - impl<$($impl_generics)*> ::core::clone::Clone for __ThePinData<$($ty_generics)*> - where $($whr)* - { - fn clone(&self) -> Self { *self } - } - - impl<$($impl_generics)*> ::core::marker::Copy for __ThePinData<$($ty_generics)*> - where $($whr)* - {} - - // Make all projection functions. - $crate::__pin_data!(make_pin_data: - @pin_data(__ThePinData), - @impl_generics($($impl_generics)*), - @ty_generics($($ty_generics)*), - @where($($whr)*), - @pinned($($pinned)*), - @not_pinned($($not_pinned)*), - ); - - // SAFETY: We have added the correct projection functions above to `__ThePinData` and - // we also use the least restrictive generics possible. - unsafe impl<$($impl_generics)*> - $crate::__internal::HasPinData for $name<$($ty_generics)*> - where $($whr)* - { - type PinData = __ThePinData<$($ty_generics)*>; - - unsafe fn __pin_data() -> Self::PinData { - __ThePinData { __phantom: ::core::marker::PhantomData } - } - } - - // SAFETY: TODO. - unsafe impl<$($impl_generics)*> - $crate::__internal::PinData for __ThePinData<$($ty_generics)*> - where $($whr)* - { - type Datee = $name<$($ty_generics)*>; - } - - // This struct will be used for the unpin analysis. Since only structurally pinned - // fields are relevant whether the struct should implement `Unpin`. - #[allow(dead_code)] - struct __Unpin <'__pin, $($impl_generics)*> - where $($whr)* - { - __phantom_pin: ::core::marker::PhantomData &'__pin ()>, - __phantom: ::core::marker::PhantomData< - fn($name<$($ty_generics)*>) -> $name<$($ty_generics)*> - >, - // Only the pinned fields. - $($pinned)* - } - - #[doc(hidden)] - impl<'__pin, $($impl_generics)*> ::core::marker::Unpin for $name<$($ty_generics)*> - where - __Unpin<'__pin, $($ty_generics)*>: ::core::marker::Unpin, - $($whr)* - {} - - // We need to disallow normal `Drop` implementation, the exact behavior depends on - // whether `PinnedDrop` was specified as the parameter. - $crate::__pin_data!(drop_prevention: - @name($name), - @impl_generics($($impl_generics)*), - @ty_generics($($ty_generics)*), - @where($($whr)*), - @pinned_drop($($pinned_drop)?), - ); - }; - }; - // When no `PinnedDrop` was specified, then we have to prevent implementing drop. - (drop_prevention: - @name($name:ident), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @where($($whr:tt)*), - @pinned_drop(), - ) => { - // We prevent this by creating a trait that will be implemented for all types implementing - // `Drop`. Additionally we will implement this trait for the struct leading to a conflict, - // if it also implements `Drop` - trait MustNotImplDrop {} - #[expect(drop_bounds)] - impl MustNotImplDrop for T {} - impl<$($impl_generics)*> MustNotImplDrop for $name<$($ty_generics)*> - where $($whr)* {} - // We also take care to prevent users from writing a useless `PinnedDrop` implementation. - // They might implement `PinnedDrop` correctly for the struct, but forget to give - // `PinnedDrop` as the parameter to `#[pin_data]`. - #[expect(non_camel_case_types)] - trait UselessPinnedDropImpl_you_need_to_specify_PinnedDrop {} - impl - UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for T {} - impl<$($impl_generics)*> - UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for $name<$($ty_generics)*> - where $($whr)* {} - }; - // When `PinnedDrop` was specified we just implement `Drop` and delegate. - (drop_prevention: - @name($name:ident), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @where($($whr:tt)*), - @pinned_drop(PinnedDrop), - ) => { - impl<$($impl_generics)*> ::core::ops::Drop for $name<$($ty_generics)*> - where $($whr)* - { - fn drop(&mut self) { - // SAFETY: Since this is a destructor, `self` will not move after this function - // terminates, since it is inaccessible. - let pinned = unsafe { ::core::pin::Pin::new_unchecked(self) }; - // SAFETY: Since this is a drop function, we can create this token to call the - // pinned destructor of this type. - let token = unsafe { $crate::__internal::OnlyCallFromDrop::new() }; - $crate::PinnedDrop::drop(pinned, token); - } - } - }; - // If some other parameter was specified, we emit a readable error. - (drop_prevention: - @name($name:ident), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @where($($whr:tt)*), - @pinned_drop($($rest:tt)*), - ) => { - compile_error!( - "Wrong parameters to `#[pin_data]`, expected nothing or `PinnedDrop`, got '{}'.", - stringify!($($rest)*), - ); - }; - (make_pin_projections: - @vis($vis:vis), - @name($name:ident), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @decl_generics($($decl_generics:tt)*), - @where($($whr:tt)*), - @pinned($($(#[$($p_attr:tt)*])* $pvis:vis $p_field:ident : $p_type:ty),* $(,)?), - @not_pinned($($(#[$($attr:tt)*])* $fvis:vis $field:ident : $type:ty),* $(,)?), - ) => { - $crate::macros::paste! { - #[doc(hidden)] - $vis struct [< $name Projection >] <'__pin, $($decl_generics)*> { - $($(#[$($p_attr)*])* $pvis $p_field : ::core::pin::Pin<&'__pin mut $p_type>,)* - $($(#[$($attr)*])* $fvis $field : &'__pin mut $type,)* - ___pin_phantom_data: ::core::marker::PhantomData<&'__pin mut ()>, - } - - impl<$($impl_generics)*> $name<$($ty_generics)*> - where $($whr)* - { - /// Pin-projects all fields of `Self`. - /// - /// These fields are structurally pinned: - $(#[doc = ::core::concat!(" - `", ::core::stringify!($p_field), "`")])* - /// - /// These fields are **not** structurally pinned: - $(#[doc = ::core::concat!(" - `", ::core::stringify!($field), "`")])* - #[inline] - $vis fn project<'__pin>( - self: ::core::pin::Pin<&'__pin mut Self>, - ) -> [< $name Projection >] <'__pin, $($ty_generics)*> { - // SAFETY: we only give access to `&mut` for fields not structurally pinned. - let this = unsafe { ::core::pin::Pin::get_unchecked_mut(self) }; - [< $name Projection >] { - $( - // SAFETY: `$p_field` is structurally pinned. - $(#[$($p_attr)*])* - $p_field : unsafe { ::core::pin::Pin::new_unchecked(&mut this.$p_field) }, - )* - $( - $(#[$($attr)*])* - $field : &mut this.$field, - )* - ___pin_phantom_data: ::core::marker::PhantomData, - } - } - } - } - }; - (make_pin_data: - @pin_data($pin_data:ident), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @where($($whr:tt)*), - @pinned($($(#[$($p_attr:tt)*])* $pvis:vis $p_field:ident : $p_type:ty),* $(,)?), - @not_pinned($($(#[$($attr:tt)*])* $fvis:vis $field:ident : $type:ty),* $(,)?), - ) => { - $crate::macros::paste! { - // For every field, we create a projection function according to its projection type. If a - // field is structurally pinned, then it must be initialized via `PinInit`, if it is not - // structurally pinned, then it can be initialized via `Init`. - // - // The functions are `unsafe` to prevent accidentally calling them. - #[allow(dead_code)] - #[expect(clippy::missing_safety_doc)] - impl<$($impl_generics)*> $pin_data<$($ty_generics)*> - where $($whr)* - { - $( - $(#[$($p_attr)*])* - $pvis unsafe fn $p_field( - self, - slot: *mut $p_type, - init: impl $crate::PinInit<$p_type, E>, - ) -> ::core::result::Result<(), E> { - // SAFETY: TODO. - unsafe { $crate::PinInit::__pinned_init(init, slot) } - } - - $(#[$($p_attr)*])* - $pvis unsafe fn [<__project_ $p_field>]<'__slot>( - self, - slot: &'__slot mut $p_type, - ) -> ::core::pin::Pin<&'__slot mut $p_type> { - ::core::pin::Pin::new_unchecked(slot) - } - )* - $( - $(#[$($attr)*])* - $fvis unsafe fn $field( - self, - slot: *mut $type, - init: impl $crate::Init<$type, E>, - ) -> ::core::result::Result<(), E> { - // SAFETY: TODO. - unsafe { $crate::Init::__init(init, slot) } - } - - $(#[$($attr)*])* - $fvis unsafe fn [<__project_ $field>]<'__slot>( - self, - slot: &'__slot mut $type, - ) -> &'__slot mut $type { - slot - } - )* - } - } - }; -} - -/// The internal init macro. Do not call manually! -/// -/// This is called by the `{try_}{pin_}init!` macros with various inputs. -/// -/// This macro has multiple internal call configurations, these are always the very first ident: -/// - nothing: this is the base case and called by the `{try_}{pin_}init!` macros. -/// - `with_update_parsed`: when the `..Zeroable::init_zeroed()` syntax has been handled. -/// - `init_slot`: recursively creates the code that initializes all fields in `slot`. -/// - `make_initializer`: recursively create the struct initializer that guarantees that every -/// field has been initialized exactly once. -#[doc(hidden)] -#[macro_export] -macro_rules! __init_internal { - ( - @this($($this:ident)?), - @typ($t:path), - @fields($($fields:tt)*), - @error($err:ty), - // Either `PinData` or `InitData`, `$use_data` should only be present in the `PinData` - // case. - @data($data:ident, $($use_data:ident)?), - // `HasPinData` or `HasInitData`. - @has_data($has_data:ident, $get_data:ident), - // `pin_init_from_closure` or `init_from_closure`. - @construct_closure($construct_closure:ident), - @munch_fields(), - ) => { - $crate::__init_internal!(with_update_parsed: - @this($($this)?), - @typ($t), - @fields($($fields)*), - @error($err), - @data($data, $($use_data)?), - @has_data($has_data, $get_data), - @construct_closure($construct_closure), - @init_zeroed(), // Nothing means default behavior. - ) - }; - ( - @this($($this:ident)?), - @typ($t:path), - @fields($($fields:tt)*), - @error($err:ty), - // Either `PinData` or `InitData`, `$use_data` should only be present in the `PinData` - // case. - @data($data:ident, $($use_data:ident)?), - // `HasPinData` or `HasInitData`. - @has_data($has_data:ident, $get_data:ident), - // `pin_init_from_closure` or `init_from_closure`. - @construct_closure($construct_closure:ident), - @munch_fields(..Zeroable::init_zeroed()), - ) => { - $crate::__init_internal!(with_update_parsed: - @this($($this)?), - @typ($t), - @fields($($fields)*), - @error($err), - @data($data, $($use_data)?), - @has_data($has_data, $get_data), - @construct_closure($construct_closure), - @init_zeroed(()), // `()` means zero all fields not mentioned. - ) - }; - ( - @this($($this:ident)?), - @typ($t:path), - @fields($($fields:tt)*), - @error($err:ty), - // Either `PinData` or `InitData`, `$use_data` should only be present in the `PinData` - // case. - @data($data:ident, $($use_data:ident)?), - // `HasPinData` or `HasInitData`. - @has_data($has_data:ident, $get_data:ident), - // `pin_init_from_closure` or `init_from_closure`. - @construct_closure($construct_closure:ident), - @munch_fields($ignore:tt $($rest:tt)*), - ) => { - $crate::__init_internal!( - @this($($this)?), - @typ($t), - @fields($($fields)*), - @error($err), - @data($data, $($use_data)?), - @has_data($has_data, $get_data), - @construct_closure($construct_closure), - @munch_fields($($rest)*), - ) - }; - (with_update_parsed: - @this($($this:ident)?), - @typ($t:path), - @fields($($fields:tt)*), - @error($err:ty), - // Either `PinData` or `InitData`, `$use_data` should only be present in the `PinData` - // case. - @data($data:ident, $($use_data:ident)?), - // `HasPinData` or `HasInitData`. - @has_data($has_data:ident, $get_data:ident), - // `pin_init_from_closure` or `init_from_closure`. - @construct_closure($construct_closure:ident), - @init_zeroed($($init_zeroed:expr)?), - ) => {{ - // We do not want to allow arbitrary returns, so we declare this type as the `Ok` return - // type and shadow it later when we insert the arbitrary user code. That way there will be - // no possibility of returning without `unsafe`. - struct __InitOk; - // Get the data about fields from the supplied type. - // - // SAFETY: TODO. - let data = unsafe { - use $crate::__internal::$has_data; - // Here we abuse `paste!` to retokenize `$t`. Declarative macros have some internal - // information that is associated to already parsed fragments, so a path fragment - // cannot be used in this position. Doing the retokenization results in valid rust - // code. - $crate::macros::paste!($t::$get_data()) - }; - // Ensure that `data` really is of type `$data` and help with type inference: - let init = $crate::__internal::$data::make_closure::<_, __InitOk, $err>( - data, - move |slot| { - { - // Shadow the structure so it cannot be used to return early. - struct __InitOk; - // If `$init_zeroed` is present we should zero the slot now and not emit an - // error when fields are missing (since they will be zeroed). We also have to - // check that the type actually implements `Zeroable`. - $({ - fn assert_zeroable(_: *mut T) {} - // Ensure that the struct is indeed `Zeroable`. - assert_zeroable(slot); - // SAFETY: The type implements `Zeroable` by the check above. - unsafe { ::core::ptr::write_bytes(slot, 0, 1) }; - $init_zeroed // This will be `()` if set. - })? - // Create the `this` so it can be referenced by the user inside of the - // expressions creating the individual fields. - $(let $this = unsafe { ::core::ptr::NonNull::new_unchecked(slot) };)? - // Initialize every field. - $crate::__init_internal!(init_slot($($use_data)?): - @data(data), - @slot(slot), - @guards(), - @munch_fields($($fields)*,), - ); - // We use unreachable code to ensure that all fields have been mentioned exactly - // once, this struct initializer will still be type-checked and complain with a - // very natural error message if a field is forgotten/mentioned more than once. - #[allow(unreachable_code, clippy::diverging_sub_expression)] - let _ = || { - $crate::__init_internal!(make_initializer: - @slot(slot), - @type_name($t), - @munch_fields($($fields)*,), - @acc(), - ); - }; - } - Ok(__InitOk) - } - ); - let init = move |slot| -> ::core::result::Result<(), $err> { - init(slot).map(|__InitOk| ()) - }; - // SAFETY: TODO. - let init = unsafe { $crate::$construct_closure::<_, $err>(init) }; - init - }}; - (init_slot($($use_data:ident)?): - @data($data:ident), - @slot($slot:ident), - @guards($($guards:ident,)*), - @munch_fields($(..Zeroable::init_zeroed())? $(,)?), - ) => { - // Endpoint of munching, no fields are left. If execution reaches this point, all fields - // have been initialized. Therefore we can now dismiss the guards by forgetting them. - $(::core::mem::forget($guards);)* - }; - (init_slot($($use_data:ident)?): - @data($data:ident), - @slot($slot:ident), - @guards($($guards:ident,)*), - // arbitrary code block - @munch_fields(_: { $($code:tt)* }, $($rest:tt)*), - ) => { - { $($code)* } - $crate::__init_internal!(init_slot($($use_data)?): - @data($data), - @slot($slot), - @guards($($guards,)*), - @munch_fields($($rest)*), - ); - }; - (init_slot($use_data:ident): // `use_data` is present, so we use the `data` to init fields. - @data($data:ident), - @slot($slot:ident), - @guards($($guards:ident,)*), - // In-place initialization syntax. - @munch_fields($field:ident <- $val:expr, $($rest:tt)*), - ) => { - let init = $val; - // Call the initializer. - // - // SAFETY: `slot` is valid, because we are inside of an initializer closure, we - // return when an error/panic occurs. - // We also use the `data` to require the correct trait (`Init` or `PinInit`) for `$field`. - unsafe { $data.$field(::core::ptr::addr_of_mut!((*$slot).$field), init)? }; - // SAFETY: - // - the project function does the correct field projection, - // - the field has been initialized, - // - the reference is only valid until the end of the initializer. - #[allow(unused_variables)] - let $field = $crate::macros::paste!(unsafe { $data.[< __project_ $field >](&mut (*$slot).$field) }); - - // Create the drop guard: - // - // We rely on macro hygiene to make it impossible for users to access this local variable. - // We use `paste!` to create new hygiene for `$field`. - $crate::macros::paste! { - // SAFETY: We forget the guard later when initialization has succeeded. - let [< __ $field _guard >] = unsafe { - $crate::__internal::DropGuard::new(::core::ptr::addr_of_mut!((*$slot).$field)) - }; - - $crate::__init_internal!(init_slot($use_data): - @data($data), - @slot($slot), - @guards([< __ $field _guard >], $($guards,)*), - @munch_fields($($rest)*), - ); - } - }; - (init_slot(): // No `use_data`, so we use `Init::__init` directly. - @data($data:ident), - @slot($slot:ident), - @guards($($guards:ident,)*), - // In-place initialization syntax. - @munch_fields($field:ident <- $val:expr, $($rest:tt)*), - ) => { - let init = $val; - // Call the initializer. - // - // SAFETY: `slot` is valid, because we are inside of an initializer closure, we - // return when an error/panic occurs. - unsafe { $crate::Init::__init(init, ::core::ptr::addr_of_mut!((*$slot).$field))? }; - - // SAFETY: - // - the field is not structurally pinned, since the line above must compile, - // - the field has been initialized, - // - the reference is only valid until the end of the initializer. - #[allow(unused_variables)] - let $field = unsafe { &mut (*$slot).$field }; - - // Create the drop guard: - // - // We rely on macro hygiene to make it impossible for users to access this local variable. - // We use `paste!` to create new hygiene for `$field`. - $crate::macros::paste! { - // SAFETY: We forget the guard later when initialization has succeeded. - let [< __ $field _guard >] = unsafe { - $crate::__internal::DropGuard::new(::core::ptr::addr_of_mut!((*$slot).$field)) - }; - - $crate::__init_internal!(init_slot(): - @data($data), - @slot($slot), - @guards([< __ $field _guard >], $($guards,)*), - @munch_fields($($rest)*), - ); - } - }; - (init_slot(): // No `use_data`, so all fields are not structurally pinned - @data($data:ident), - @slot($slot:ident), - @guards($($guards:ident,)*), - // Init by-value. - @munch_fields($field:ident $(: $val:expr)?, $($rest:tt)*), - ) => { - { - $(let $field = $val;)? - // Initialize the field. - // - // SAFETY: The memory at `slot` is uninitialized. - unsafe { ::core::ptr::write(::core::ptr::addr_of_mut!((*$slot).$field), $field) }; - } - - #[allow(unused_variables)] - // SAFETY: - // - the field is not structurally pinned, since no `use_data` was required to create this - // initializer, - // - the field has been initialized, - // - the reference is only valid until the end of the initializer. - let $field = unsafe { &mut (*$slot).$field }; - - // Create the drop guard: - // - // We rely on macro hygiene to make it impossible for users to access this local variable. - // We use `paste!` to create new hygiene for `$field`. - $crate::macros::paste! { - // SAFETY: We forget the guard later when initialization has succeeded. - let [< __ $field _guard >] = unsafe { - $crate::__internal::DropGuard::new(::core::ptr::addr_of_mut!((*$slot).$field)) - }; - - $crate::__init_internal!(init_slot(): - @data($data), - @slot($slot), - @guards([< __ $field _guard >], $($guards,)*), - @munch_fields($($rest)*), - ); - } - }; - (init_slot($use_data:ident): - @data($data:ident), - @slot($slot:ident), - @guards($($guards:ident,)*), - // Init by-value. - @munch_fields($field:ident $(: $val:expr)?, $($rest:tt)*), - ) => { - { - $(let $field = $val;)? - // Initialize the field. - // - // SAFETY: The memory at `slot` is uninitialized. - unsafe { ::core::ptr::write(::core::ptr::addr_of_mut!((*$slot).$field), $field) }; - } - // SAFETY: - // - the project function does the correct field projection, - // - the field has been initialized, - // - the reference is only valid until the end of the initializer. - #[allow(unused_variables)] - let $field = $crate::macros::paste!(unsafe { $data.[< __project_ $field >](&mut (*$slot).$field) }); - - // Create the drop guard: - // - // We rely on macro hygiene to make it impossible for users to access this local variable. - // We use `paste!` to create new hygiene for `$field`. - $crate::macros::paste! { - // SAFETY: We forget the guard later when initialization has succeeded. - let [< __ $field _guard >] = unsafe { - $crate::__internal::DropGuard::new(::core::ptr::addr_of_mut!((*$slot).$field)) - }; - - $crate::__init_internal!(init_slot($use_data): - @data($data), - @slot($slot), - @guards([< __ $field _guard >], $($guards,)*), - @munch_fields($($rest)*), - ); - } - }; - (make_initializer: - @slot($slot:ident), - @type_name($t:path), - @munch_fields(_: { $($code:tt)* }, $($rest:tt)*), - @acc($($acc:tt)*), - ) => { - // code blocks are ignored for the initializer check - $crate::__init_internal!(make_initializer: - @slot($slot), - @type_name($t), - @munch_fields($($rest)*), - @acc($($acc)*), - ); - }; - (make_initializer: - @slot($slot:ident), - @type_name($t:path), - @munch_fields(..Zeroable::init_zeroed() $(,)?), - @acc($($acc:tt)*), - ) => { - // Endpoint, nothing more to munch, create the initializer. Since the users specified - // `..Zeroable::init_zeroed()`, the slot will already have been zeroed and all field that have - // not been overwritten are thus zero and initialized. We still check that all fields are - // actually accessible by using the struct update syntax ourselves. - // We are inside of a closure that is never executed and thus we can abuse `slot` to - // get the correct type inference here: - #[allow(unused_assignments)] - unsafe { - let mut zeroed = ::core::mem::zeroed(); - // We have to use type inference here to make zeroed have the correct type. This does - // not get executed, so it has no effect. - ::core::ptr::write($slot, zeroed); - zeroed = ::core::mem::zeroed(); - // Here we abuse `paste!` to retokenize `$t`. Declarative macros have some internal - // information that is associated to already parsed fragments, so a path fragment - // cannot be used in this position. Doing the retokenization results in valid rust - // code. - $crate::macros::paste!( - ::core::ptr::write($slot, $t { - $($acc)* - ..zeroed - }); - ); - } - }; - (make_initializer: - @slot($slot:ident), - @type_name($t:path), - @munch_fields($(,)?), - @acc($($acc:tt)*), - ) => { - // Endpoint, nothing more to munch, create the initializer. - // Since we are in the closure that is never called, this will never get executed. - // We abuse `slot` to get the correct type inference here: - // - // SAFETY: TODO. - unsafe { - // Here we abuse `paste!` to retokenize `$t`. Declarative macros have some internal - // information that is associated to already parsed fragments, so a path fragment - // cannot be used in this position. Doing the retokenization results in valid rust - // code. - $crate::macros::paste!( - ::core::ptr::write($slot, $t { - $($acc)* - }); - ); - } - }; - (make_initializer: - @slot($slot:ident), - @type_name($t:path), - @munch_fields($field:ident <- $val:expr, $($rest:tt)*), - @acc($($acc:tt)*), - ) => { - $crate::__init_internal!(make_initializer: - @slot($slot), - @type_name($t), - @munch_fields($($rest)*), - @acc($($acc)* $field: ::core::panic!(),), - ); - }; - (make_initializer: - @slot($slot:ident), - @type_name($t:path), - @munch_fields($field:ident $(: $val:expr)?, $($rest:tt)*), - @acc($($acc:tt)*), - ) => { - $crate::__init_internal!(make_initializer: - @slot($slot), - @type_name($t), - @munch_fields($($rest)*), - @acc($($acc)* $field: ::core::panic!(),), - ); - }; -} - -#[doc(hidden)] -#[macro_export] -macro_rules! __derive_zeroable { - (parse_input: - @sig( - $(#[$($struct_attr:tt)*])* - $vis:vis struct $name:ident - $(where $($whr:tt)*)? - ), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @body({ - $( - $(#[$($field_attr:tt)*])* - $field_vis:vis $field:ident : $field_ty:ty - ),* $(,)? - }), - ) => { - // SAFETY: Every field type implements `Zeroable` and padding bytes may be zero. - #[automatically_derived] - unsafe impl<$($impl_generics)*> $crate::Zeroable for $name<$($ty_generics)*> - where - $($($whr)*)? - {} - const _: () = { - fn assert_zeroable() {} - fn ensure_zeroable<$($impl_generics)*>() - where $($($whr)*)? - { - $(assert_zeroable::<$field_ty>();)* - } - }; - }; - (parse_input: - @sig( - $(#[$($struct_attr:tt)*])* - $vis:vis union $name:ident - $(where $($whr:tt)*)? - ), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @body({ - $( - $(#[$($field_attr:tt)*])* - $field_vis:vis $field:ident : $field_ty:ty - ),* $(,)? - }), - ) => { - // SAFETY: Every field type implements `Zeroable` and padding bytes may be zero. - #[automatically_derived] - unsafe impl<$($impl_generics)*> $crate::Zeroable for $name<$($ty_generics)*> - where - $($($whr)*)? - {} - const _: () = { - fn assert_zeroable() {} - fn ensure_zeroable<$($impl_generics)*>() - where $($($whr)*)? - { - $(assert_zeroable::<$field_ty>();)* - } - }; - }; -} - -#[doc(hidden)] -#[macro_export] -macro_rules! __maybe_derive_zeroable { - (parse_input: - @sig( - $(#[$($struct_attr:tt)*])* - $vis:vis struct $name:ident - $(where $($whr:tt)*)? - ), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @body({ - $( - $(#[$($field_attr:tt)*])* - $field_vis:vis $field:ident : $field_ty:ty - ),* $(,)? - }), - ) => { - // SAFETY: Every field type implements `Zeroable` and padding bytes may be zero. - #[automatically_derived] - unsafe impl<$($impl_generics)*> $crate::Zeroable for $name<$($ty_generics)*> - where - $( - // the `for<'__dummy>` HRTB makes this not error without the `trivial_bounds` - // feature . - $field_ty: for<'__dummy> $crate::Zeroable, - )* - $($($whr)*)? - {} - }; - (parse_input: - @sig( - $(#[$($struct_attr:tt)*])* - $vis:vis union $name:ident - $(where $($whr:tt)*)? - ), - @impl_generics($($impl_generics:tt)*), - @ty_generics($($ty_generics:tt)*), - @body({ - $( - $(#[$($field_attr:tt)*])* - $field_vis:vis $field:ident : $field_ty:ty - ),* $(,)? - }), - ) => { - // SAFETY: Every field type implements `Zeroable` and padding bytes may be zero. - #[automatically_derived] - unsafe impl<$($impl_generics)*> $crate::Zeroable for $name<$($ty_generics)*> - where - $( - // the `for<'__dummy>` HRTB makes this not error without the `trivial_bounds` - // feature . - $field_ty: for<'__dummy> $crate::Zeroable, - )* - $($($whr)*)? - {} - }; -} diff --git a/tests/default_error.rs b/tests/default_error.rs new file mode 100644 index 00000000..dc357626 --- /dev/null +++ b/tests/default_error.rs @@ -0,0 +1,16 @@ +#![allow(dead_code)] + +use pin_init::{init, Init}; + +struct Foo {} + +struct Error; + +impl Foo { + fn new() -> impl Init { + init!( + #[default_error(Error)] + Foo {} + ) + } +} diff --git a/tests/many_generics.rs b/tests/many_generics.rs index 2cad6421..d45ac287 100644 --- a/tests/many_generics.rs +++ b/tests/many_generics.rs @@ -1,4 +1,5 @@ #![cfg_attr(not(RUSTC_LINT_REASONS_IS_STABLE), feature(lint_reasons))] +#![allow(dead_code)] use core::{marker::PhantomPinned, pin::Pin}; use pin_init::*; diff --git a/tests/ring_buf.rs b/tests/ring_buf.rs index 1d21ce1e..9029f511 100644 --- a/tests/ring_buf.rs +++ b/tests/ring_buf.rs @@ -166,7 +166,7 @@ pub struct EvenU64 { impl EvenU64 { #[allow(clippy::manual_is_multiple_of)] pub fn new2(value: u64) -> impl Init { - try_init!(Self { + init!(Self { info: "Hello world!".to_owned(), data: if value % 2 == 0 { value @@ -178,7 +178,7 @@ impl EvenU64 { #[allow(clippy::manual_is_multiple_of)] pub fn new(value: u64) -> impl Init { - try_init!(Self { + init!(Self { info: "Hello world!".to_owned(), data: if value % 2 == 0 { value diff --git a/tests/ui/compile-fail/init/colon_instead_of_arrow.stderr b/tests/ui/compile-fail/init/colon_instead_of_arrow.stderr index f7be4a2f..f5d9ee7b 100644 --- a/tests/ui/compile-fail/init/colon_instead_of_arrow.stderr +++ b/tests/ui/compile-fail/init/colon_instead_of_arrow.stderr @@ -1,14 +1,13 @@ error[E0308]: mismatched types - --> tests/ui/compile-fail/init/colon_instead_of_arrow.rs:21:9 + --> tests/ui/compile-fail/init/colon_instead_of_arrow.rs:21:31 | 14 | fn new() -> impl PinInit { | ------------------ the found opaque type ... 21 | pin_init!(Self { bar: Bar::new() }) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | expected `Bar`, found opaque type - | arguments to this function are incorrect + | --- ^^^^^^^^^^ expected `Bar`, found opaque type + | | + | arguments to this function are incorrect | = note: expected struct `Bar` found opaque type `impl pin_init::PinInit` @@ -17,4 +16,3 @@ note: function defined here | | pub const unsafe fn write(dst: *mut T, src: T) { | ^^^^^ - = note: this error originates in the macro `$crate::__init_internal` which comes from the expansion of the macro `pin_init` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/compile-fail/init/data_access.rs b/tests/ui/compile-fail/init/data_access.rs new file mode 100644 index 00000000..d8574aa1 --- /dev/null +++ b/tests/ui/compile-fail/init/data_access.rs @@ -0,0 +1,12 @@ +use pin_init::*; + +#[pin_data] +struct Foo {} + +fn main() { + let _ = pin_init!(Foo { + _: { + let _ = __data; + }, + }); +} diff --git a/tests/ui/compile-fail/init/data_access.stderr b/tests/ui/compile-fail/init/data_access.stderr new file mode 100644 index 00000000..c14114b4 --- /dev/null +++ b/tests/ui/compile-fail/init/data_access.stderr @@ -0,0 +1,5 @@ +error[E0425]: cannot find value `__data` in this scope + --> tests/ui/compile-fail/init/data_access.rs:9:21 + | +9 | let _ = __data; + | ^^^^^^ not found in this scope diff --git a/tests/ui/compile-fail/init/field_value_wrong_type.stderr b/tests/ui/compile-fail/init/field_value_wrong_type.stderr index 5b91b5c7..f6470767 100644 --- a/tests/ui/compile-fail/init/field_value_wrong_type.stderr +++ b/tests/ui/compile-fail/init/field_value_wrong_type.stderr @@ -1,15 +1,13 @@ error[E0308]: mismatched types - --> tests/ui/compile-fail/init/field_value_wrong_type.rs:8:13 + --> tests/ui/compile-fail/init/field_value_wrong_type.rs:8:28 | 8 | let _ = init!(Foo { a: () }); - | ^^^^^^^^^^^^^^^^^^^^ - | | - | expected `usize`, found `()` - | arguments to this function are incorrect + | - ^^ expected `usize`, found `()` + | | + | arguments to this function are incorrect | note: function defined here --> $RUST/core/src/ptr/mod.rs | | pub const unsafe fn write(dst: *mut T, src: T) { | ^^^^^ - = note: this error originates in the macro `$crate::__init_internal` which comes from the expansion of the macro `init` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/compile-fail/init/guard_access.rs b/tests/ui/compile-fail/init/guard_access.rs new file mode 100644 index 00000000..bc837e55 --- /dev/null +++ b/tests/ui/compile-fail/init/guard_access.rs @@ -0,0 +1,14 @@ +use pin_init::*; + +struct Foo { + x: usize, +} + +fn main() { + let _ = init!(Foo { + x: 0, + _: { + let _ = __x_guard; + }, + }); +} diff --git a/tests/ui/compile-fail/init/guard_access.stderr b/tests/ui/compile-fail/init/guard_access.stderr new file mode 100644 index 00000000..687df536 --- /dev/null +++ b/tests/ui/compile-fail/init/guard_access.stderr @@ -0,0 +1,5 @@ +error[E0425]: cannot find value `__x_guard` in this scope + --> tests/ui/compile-fail/init/guard_access.rs:11:21 + | +11 | let _ = __x_guard; + | ^^^^^^^^^ not found in this scope diff --git a/tests/ui/compile-fail/init/invalid_init.stderr b/tests/ui/compile-fail/init/invalid_init.stderr index b80864de..c38e6be2 100644 --- a/tests/ui/compile-fail/init/invalid_init.stderr +++ b/tests/ui/compile-fail/init/invalid_init.stderr @@ -1,16 +1,13 @@ error[E0277]: the trait bound `impl pin_init::PinInit: Init` is not satisfied - --> tests/ui/compile-fail/init/invalid_init.rs:18:13 + --> tests/ui/compile-fail/init/invalid_init.rs:19:16 | 18 | let _ = init!(Foo { - | _____________^ + | _____________- 19 | | bar <- Bar::new(), + | | ^^^^^^^^^^ the trait `Init` is not implemented for `impl pin_init::PinInit` 20 | | }); - | | ^ - | | | - | |______the trait `Init` is not implemented for `impl pin_init::PinInit` - | required by a bound introduced by this call + | |______- required by a bound introduced by this call | = help: the following other types implement trait `Init`: ChainInit Result - = note: this error originates in the macro `$crate::__init_internal` which comes from the expansion of the macro `init` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/compile-fail/init/missing_comma.stderr b/tests/ui/compile-fail/init/missing_comma.stderr index 9e4c25db..dd16d94b 100644 --- a/tests/ui/compile-fail/init/missing_comma.stderr +++ b/tests/ui/compile-fail/init/missing_comma.stderr @@ -1,23 +1,5 @@ -error: no rules expected `c` +error: expected `}` or `,` --> tests/ui/compile-fail/init/missing_comma.rs:16:9 | 16 | c: Bar, - | ^ no rules expected this token in macro call - | -note: while trying to match `,` - --> src/macros.rs - | - | @munch_fields($field:ident $(: $val:expr)?, $($rest:tt)*), - | ^ - -error: no rules expected `c` - --> tests/ui/compile-fail/init/missing_comma.rs:16:9 - | -16 | c: Bar, - | ^ no rules expected this token in macro call - | -note: while trying to match `,` - --> src/macros.rs - | - | @munch_fields($field:ident $(: $val:expr)?, $($rest:tt)*), - | ^ + | ^ diff --git a/tests/ui/compile-fail/init/missing_comma_with_zeroable.stderr b/tests/ui/compile-fail/init/missing_comma_with_zeroable.stderr index 51ce37b1..865615d2 100644 --- a/tests/ui/compile-fail/init/missing_comma_with_zeroable.stderr +++ b/tests/ui/compile-fail/init/missing_comma_with_zeroable.stderr @@ -1,14 +1,10 @@ error[E0308]: mismatched types - --> tests/ui/compile-fail/init/missing_comma_with_zeroable.rs:11:13 + --> tests/ui/compile-fail/init/missing_comma_with_zeroable.rs:12:12 | -11 | let _ = init!(Foo { - | _____________^ -12 | | a: 0..Zeroable::init_zeroed() -13 | | }); - | | ^ - | | | - | |______expected `usize`, found `Range<{integer}>` - | arguments to this function are incorrect +12 | a: 0..Zeroable::init_zeroed() + | - ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `Range<{integer}>` + | | + | arguments to this function are incorrect | = note: expected type `usize` found struct `std::ops::Range<{integer}>` @@ -17,7 +13,6 @@ note: function defined here | | pub const unsafe fn write(dst: *mut T, src: T) { | ^^^^^ - = note: this error originates in the macro `$crate::__init_internal` which comes from the expansion of the macro `init` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0063]: missing field `b` in initializer of `Foo` --> tests/ui/compile-fail/init/missing_comma_with_zeroable.rs:11:19 diff --git a/tests/ui/compile-fail/init/missing_error_type.rs b/tests/ui/compile-fail/init/missing_error_type.rs index 13fcf9a7..352a94b5 100644 --- a/tests/ui/compile-fail/init/missing_error_type.rs +++ b/tests/ui/compile-fail/init/missing_error_type.rs @@ -5,5 +5,5 @@ struct Foo { } fn main() { - let _ = try_init!(Foo { x: Box::new(0)? }?); + let _ = init!(Foo { x: Box::new(0)? }?); } diff --git a/tests/ui/compile-fail/init/missing_error_type.stderr b/tests/ui/compile-fail/init/missing_error_type.stderr index 8f9f3ef3..9e9a1478 100644 --- a/tests/ui/compile-fail/init/missing_error_type.stderr +++ b/tests/ui/compile-fail/init/missing_error_type.stderr @@ -1,11 +1,7 @@ -error: unexpected end of macro invocation - --> tests/ui/compile-fail/init/missing_error_type.rs:8:47 +error: unexpected end of input, expected one of: `for`, parentheses, `fn`, `unsafe`, `extern`, identifier, `::`, `<`, `dyn`, square brackets, `*`, `&`, `!`, `impl`, `_`, lifetime + --> tests/ui/compile-fail/init/missing_error_type.rs:8:13 | -8 | let _ = try_init!(Foo { x: Box::new(0)? }?); - | ^ missing tokens in macro arguments +8 | let _ = init!(Foo { x: Box::new(0)? }?); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: while trying to match meta-variable `$err:ty` - --> src/lib.rs - | - | }? $err:ty) => { - | ^^^^^^^ + = note: this error originates in the macro `init` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/compile-fail/init/missing_field.rs b/tests/ui/compile-fail/init/missing_field.rs index 048d067b..ec2f3f7a 100644 --- a/tests/ui/compile-fail/init/missing_field.rs +++ b/tests/ui/compile-fail/init/missing_field.rs @@ -9,7 +9,7 @@ struct Foo { fn main() { let _foo = pin_init!(Foo { a: 0 }); - let _foo = try_pin_init!(Foo { a: 0 }? ::std::convert::Infallible); + let _foo = pin_init!(Foo { a: 0 }? ::std::convert::Infallible); let _foo = init!(Foo { a: 0 }); - let _foo = try_init!(Foo { a: 0 }? ::std::convert::Infallible); + let _foo = init!(Foo { a: 0 }? ::std::convert::Infallible); } diff --git a/tests/ui/compile-fail/init/missing_field.stderr b/tests/ui/compile-fail/init/missing_field.stderr index 14dcded1..4a6c1d89 100644 --- a/tests/ui/compile-fail/init/missing_field.stderr +++ b/tests/ui/compile-fail/init/missing_field.stderr @@ -5,10 +5,10 @@ error[E0063]: missing field `b` in initializer of `Foo` | ^^^ missing `b` error[E0063]: missing field `b` in initializer of `Foo` - --> tests/ui/compile-fail/init/missing_field.rs:12:30 + --> tests/ui/compile-fail/init/missing_field.rs:12:26 | -12 | let _foo = try_pin_init!(Foo { a: 0 }? ::std::convert::Infallible); - | ^^^ missing `b` +12 | let _foo = pin_init!(Foo { a: 0 }? ::std::convert::Infallible); + | ^^^ missing `b` error[E0063]: missing field `b` in initializer of `Foo` --> tests/ui/compile-fail/init/missing_field.rs:13:22 @@ -17,7 +17,7 @@ error[E0063]: missing field `b` in initializer of `Foo` | ^^^ missing `b` error[E0063]: missing field `b` in initializer of `Foo` - --> tests/ui/compile-fail/init/missing_field.rs:14:26 + --> tests/ui/compile-fail/init/missing_field.rs:14:22 | -14 | let _foo = try_init!(Foo { a: 0 }? ::std::convert::Infallible); - | ^^^ missing `b` +14 | let _foo = init!(Foo { a: 0 }? ::std::convert::Infallible); + | ^^^ missing `b` diff --git a/tests/ui/compile-fail/init/missing_pin_data.stderr b/tests/ui/compile-fail/init/missing_pin_data.stderr index 22f34abd..1215692b 100644 --- a/tests/ui/compile-fail/init/missing_pin_data.stderr +++ b/tests/ui/compile-fail/init/missing_pin_data.stderr @@ -10,4 +10,4 @@ error[E0599]: no associated item named `__pin_data` found for struct `Foo` in th = help: items from traits can only be used if the trait is implemented and in scope = note: the following trait defines an item `__pin_data`, perhaps you need to implement it: candidate #1: `HasPinData` - = note: this error originates in the macro `$crate::try_pin_init` which comes from the expansion of the macro `pin_init` (in Nightly builds, run with -Z macro-backtrace for more info) + = 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/init/no_error_coercion.rs b/tests/ui/compile-fail/init/no_error_coercion.rs index 5f6844aa..818eacb5 100644 --- a/tests/ui/compile-fail/init/no_error_coercion.rs +++ b/tests/ui/compile-fail/init/no_error_coercion.rs @@ -13,7 +13,7 @@ struct Bar { impl Foo { fn new() -> impl Init { - try_init!(Self { + init!(Self { a: Box::new(42), bar <- init!(Bar { b: 42 }), }? AllocError) diff --git a/tests/ui/compile-fail/init/no_error_coercion.stderr b/tests/ui/compile-fail/init/no_error_coercion.stderr index dae2783e..974c3c15 100644 --- a/tests/ui/compile-fail/init/no_error_coercion.stderr +++ b/tests/ui/compile-fail/init/no_error_coercion.stderr @@ -1,14 +1,14 @@ error[E0277]: `?` couldn't convert the error to `std::alloc::AllocError` - --> tests/ui/compile-fail/init/no_error_coercion.rs:16:9 + --> tests/ui/compile-fail/init/no_error_coercion.rs:19:22 | -16 | / try_init!(Self { +16 | / init!(Self { 17 | | a: Box::new(42), 18 | | bar <- init!(Bar { b: 42 }), 19 | | }? AllocError) | | ^ | | | - | |______________________this can't be annotated with `?` because it has type `Result<_, Infallible>` - | the trait `From` is not implemented for `std::alloc::AllocError` + | |______________________the trait `From` is not implemented for `std::alloc::AllocError` + | this can't be annotated with `?` because it has type `Result<_, Infallible>` | = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait - = note: this error originates in the macro `$crate::__init_internal` which comes from the expansion of the macro `try_init` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `init` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/compile-fail/init/shadowing_field.stderr b/tests/ui/compile-fail/init/shadowing_field.stderr index 4846adb0..d9ba3f59 100644 --- a/tests/ui/compile-fail/init/shadowing_field.stderr +++ b/tests/ui/compile-fail/init/shadowing_field.stderr @@ -1,15 +1,17 @@ error[E0308]: mismatched types - --> tests/ui/compile-fail/init/shadowing_field.rs:10:13 + --> tests/ui/compile-fail/init/shadowing_field.rs:10:31 | 10 | let _ = init!(Foo { x, y: x }); - | ^^^^^^^^^^^^^^^^^^^^^^ - | | - | expected `usize`, found `&mut usize` - | arguments to this function are incorrect + | - ^ expected `usize`, found `&mut usize` + | | + | arguments to this function are incorrect | note: function defined here --> $RUST/core/src/ptr/mod.rs | | pub const unsafe fn write(dst: *mut T, src: T) { | ^^^^^ - = note: this error originates in the macro `$crate::__init_internal` which comes from the expansion of the macro `init` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider dereferencing the borrow + | +10 | let _ = init!(Foo { x, y: *x }); + | + diff --git a/tests/ui/compile-fail/init/wrong_generics.stderr b/tests/ui/compile-fail/init/wrong_generics.stderr index cb1b1ef5..03960aa1 100644 --- a/tests/ui/compile-fail/init/wrong_generics.stderr +++ b/tests/ui/compile-fail/init/wrong_generics.stderr @@ -1,11 +1,19 @@ -error: no rules expected `<` +error: comparison operators cannot be chained --> tests/ui/compile-fail/init/wrong_generics.rs:7:22 | 7 | let _ = init!(Foo<()> { - | ^ no rules expected this token in macro call + | ^ ^ | -note: while trying to match `{` - --> src/lib.rs +help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments | - | ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { - | ^ +7 | let _ = init!(Foo::<()> { + | ++ + +error: comparison operators cannot be chained + --> tests/ui/compile-fail/init/wrong_generics.rs:7:22 + | +7 | let _ = init!(Foo<()> { + | ^ ^ + | + = help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments + = help: or use `(...)` if you meant to specify fn arguments diff --git a/tests/ui/compile-fail/init/wrong_generics2.stderr b/tests/ui/compile-fail/init/wrong_generics2.stderr index e8eab623..66edc9c3 100644 --- a/tests/ui/compile-fail/init/wrong_generics2.stderr +++ b/tests/ui/compile-fail/init/wrong_generics2.stderr @@ -1,51 +1,3 @@ -error: struct literal body without path - --> tests/ui/compile-fail/init/wrong_generics2.rs:7:13 - | -7 | let _ = init!(Foo::<(), ()> { - | _____________^ -8 | | value <- (), -9 | | }); - | |______^ - | - = note: this error originates in the macro `$crate::__init_internal` which comes from the expansion of the macro `init` (in Nightly builds, run with -Z macro-backtrace for more info) -help: you might have forgotten to add the struct literal inside the block - --> src/macros.rs - | - ~ ::core::ptr::write($slot, $t { SomeStruct { - |2 $($acc)* - ~ } }); - | - -error: expected one of `)`, `,`, `.`, `?`, or an operator, found `{` - --> tests/ui/compile-fail/init/wrong_generics2.rs:7:13 - | -7 | let _ = init!(Foo::<(), ()> { - | _____________^ -8 | | value <- (), -9 | | }); - | | ^ - | | | - | |______expected one of `)`, `,`, `.`, `?`, or an operator - | help: missing `,` - | - = note: this error originates in the macro `$crate::__init_internal` which comes from the expansion of the macro `init` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0423]: expected value, found struct `Foo` - --> tests/ui/compile-fail/init/wrong_generics2.rs:7:13 - | -3 | / struct Foo { -4 | | value: T, -5 | | } - | |_- `Foo` defined here -6 | fn main() { -7 | let _ = init!(Foo::<(), ()> { - | _____________^ -8 | | value <- (), -9 | | }); - | |______^ help: use struct literal syntax instead: `Foo { value: val }` - | - = note: this error originates in the macro `$crate::try_init` which comes from the expansion of the macro `init` (in Nightly builds, run with -Z macro-backtrace for more info) - error[E0107]: struct takes 1 generic argument but 2 generic arguments were supplied --> tests/ui/compile-fail/init/wrong_generics2.rs:7:19 | @@ -59,19 +11,3 @@ note: struct defined here, with 1 generic parameter: `T` | 3 | struct Foo { | ^^^ - - -error[E0061]: this function takes 2 arguments but 3 arguments were supplied - --> tests/ui/compile-fail/init/wrong_generics2.rs:7:13 - | -7 | let _ = init!(Foo::<(), ()> { - | _____________^ -8 | | value <- (), -9 | | }); - | |______^ unexpected argument #3 - | -note: function defined here - --> $RUST/core/src/ptr/mod.rs - | - | pub const unsafe fn write(dst: *mut T, src: T) { - | ^^^^^ - = note: this error originates in the macro `$crate::__init_internal` which comes from the expansion of the macro `init` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/compile-fail/pin_data/missing_comma.stderr b/tests/ui/compile-fail/pin_data/missing_comma.stderr index 5e296742..1b50f815 100644 --- a/tests/ui/compile-fail/pin_data/missing_comma.stderr +++ b/tests/ui/compile-fail/pin_data/missing_comma.stderr @@ -4,11 +4,14 @@ error: expected `,`, or `}`, found `b` 5 | a: Box | ^ help: try adding a comma: `,` -error: recursion limit reached while expanding `$crate::__pin_data!` - --> tests/ui/compile-fail/pin_data/missing_comma.rs:3:1 +error: expected `,` + --> tests/ui/compile-fail/pin_data/missing_comma.rs:6:5 | -3 | #[pin_data] - | ^^^^^^^^^^^ +6 | b: Box + | ^ + +error[E0601]: `main` function not found in crate `$CRATE` + --> tests/ui/compile-fail/pin_data/missing_comma.rs:7:2 | - = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`$CRATE`) - = note: this error originates in the macro `$crate::__pin_data` which comes from the expansion of the attribute macro `pin_data` (in Nightly builds, run with -Z macro-backtrace for more info) +7 | } + | ^ consider adding a `main` function to `$DIR/tests/ui/compile-fail/pin_data/missing_comma.rs` diff --git a/tests/ui/compile-fail/pin_data/missing_pin.stderr b/tests/ui/compile-fail/pin_data/missing_pin.stderr index fdc96d73..4bd17fbb 100644 --- a/tests/ui/compile-fail/pin_data/missing_pin.stderr +++ b/tests/ui/compile-fail/pin_data/missing_pin.stderr @@ -1,11 +1,10 @@ error[E0277]: the trait bound `impl PinInit: Init` is not satisfied - --> tests/ui/compile-fail/pin_data/missing_pin.rs:11:9 + --> tests/ui/compile-fail/pin_data/missing_pin.rs:12:18 | -11 | / pin_init!(Self { -12 | | a <- a, - | | - required by a bound introduced by this call -13 | | }) - | |__________^ the trait `Init` is not implemented for `impl PinInit` +12 | a <- a, + | - ^ the trait `Init` is not implemented for `impl PinInit` + | | + | required by a bound introduced by this call | = help: the trait `Init` is not implemented for `impl PinInit` but trait `Init, Infallible>` is implemented for it @@ -18,4 +17,4 @@ note: required by a bound in `__ThePinData::a` 5 | struct Foo { 6 | a: usize, | - required by a bound in this associated function - = note: this error originates in the macro `$crate::__pin_data` which comes from the expansion of the attribute macro `pin_data` (in Nightly builds, run with -Z macro-backtrace for more info) + = 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/no_pin_on_phantompinned.stderr b/tests/ui/compile-fail/pin_data/no_pin_on_phantompinned.stderr index 4c1b7ce3..e334cfef 100644 --- a/tests/ui/compile-fail/pin_data/no_pin_on_phantompinned.stderr +++ b/tests/ui/compile-fail/pin_data/no_pin_on_phantompinned.stderr @@ -1,31 +1,46 @@ -error: The field `pin1` of type `PhantomPinned` only has an effect, if it has the `#[pin]` attribute. - --> tests/ui/compile-fail/pin_data/no_pin_on_phantompinned.rs:4:1 +error: The field `pin1` of type `PhantomData` only has an effect if it has the `#[pin]` attribute + --> tests/ui/compile-fail/pin_data/no_pin_on_phantompinned.rs:6:5 | -4 | #[pin_data] - | ^^^^^^^^^^^ +6 | pin1: PhantomPinned, + | ^^^^^^^^^^^^^^^^^^^ + +error: The field `pin2` of type `PhantomData` only has an effect if it has the `#[pin]` attribute + --> tests/ui/compile-fail/pin_data/no_pin_on_phantompinned.rs:7:5 | - = note: this error originates in the macro `$crate::__pin_data` which comes from the expansion of the attribute macro `pin_data` (in Nightly builds, run with -Z macro-backtrace for more info) +7 | pin2: marker::PhantomPinned, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: The field `pin2` of type `PhantomPinned` only has an effect, if it has the `#[pin]` attribute. - --> tests/ui/compile-fail/pin_data/no_pin_on_phantompinned.rs:4:1 +error: The field `pin3` of type `PhantomData` only has an effect if it has the `#[pin]` attribute + --> tests/ui/compile-fail/pin_data/no_pin_on_phantompinned.rs:8:5 | -4 | #[pin_data] - | ^^^^^^^^^^^ +8 | pin3: core::marker::PhantomPinned, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: The field `pin4` of type `PhantomData` only has an effect if it has the `#[pin]` attribute + --> tests/ui/compile-fail/pin_data/no_pin_on_phantompinned.rs:9:5 | - = note: this error originates in the macro `$crate::__pin_data` which comes from the expansion of the attribute macro `pin_data` (in Nightly builds, run with -Z macro-backtrace for more info) +9 | pin4: ::core::marker::PhantomPinned, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: The field `pin3` of type `PhantomPinned` only has an effect, if it has the `#[pin]` attribute. - --> tests/ui/compile-fail/pin_data/no_pin_on_phantompinned.rs:4:1 +error[E0412]: cannot find type `PhantomPinned` in this scope + --> tests/ui/compile-fail/pin_data/no_pin_on_phantompinned.rs:6:11 + | +6 | pin1: PhantomPinned, + | ^^^^^^^^^^^^^ not found in this scope | -4 | #[pin_data] - | ^^^^^^^^^^^ +help: consider importing this struct + | +1 + use std::marker::PhantomPinned; | - = note: this error originates in the macro `$crate::__pin_data` which comes from the expansion of the attribute macro `pin_data` (in Nightly builds, run with -Z macro-backtrace for more info) -error: The field `pin4` of type `PhantomPinned` only has an effect, if it has the `#[pin]` attribute. - --> tests/ui/compile-fail/pin_data/no_pin_on_phantompinned.rs:4:1 +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `marker` + --> tests/ui/compile-fail/pin_data/no_pin_on_phantompinned.rs:7:11 + | +7 | pin2: marker::PhantomPinned, + | ^^^^^^ use of unresolved module or unlinked crate `marker` + | + = help: if you wanted to use a crate named `marker`, use `cargo add marker` to add it to your `Cargo.toml` +help: consider importing this module | -4 | #[pin_data] - | ^^^^^^^^^^^ +1 + use std::marker; | - = note: this error originates in the macro `$crate::__pin_data` which comes from the expansion of 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/pin_data_but_drop.stderr b/tests/ui/compile-fail/pin_data/pin_data_but_drop.stderr index 6143d9af..b46a30a1 100644 --- a/tests/ui/compile-fail/pin_data/pin_data_but_drop.stderr +++ b/tests/ui/compile-fail/pin_data/pin_data_but_drop.stderr @@ -7,4 +7,4 @@ error[E0119]: conflicting implementations of trait `MustNotImplDrop` for type `F | first implementation here | conflicting implementation for `Foo` | - = note: this error originates in the macro `$crate::__pin_data` which comes from the expansion of the attribute macro `pin_data` (in Nightly builds, run with -Z macro-backtrace for more info) + = 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/twice.stderr b/tests/ui/compile-fail/pin_data/twice.stderr index c09a6ce7..274912e3 100644 --- a/tests/ui/compile-fail/pin_data/twice.stderr +++ b/tests/ui/compile-fail/pin_data/twice.stderr @@ -1,54 +1,50 @@ error[E0428]: the name `FooProjection` is defined multiple times - --> tests/ui/compile-fail/pin_data/twice.rs:3:1 + --> tests/ui/compile-fail/pin_data/twice.rs:4:1 | 3 | #[pin_data] - | ^^^^^^^^^^^ - | | - | `FooProjection` redefined here - | previous definition of the type `FooProjection` here + | ----------- 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 macro `$crate::__pin_data` which comes from the expansion of the attribute macro `pin_data` (in Nightly builds, run with -Z macro-backtrace for more info) + = 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 `HasPinData` for type `Foo` - --> tests/ui/compile-fail/pin_data/twice.rs:3:1 + --> tests/ui/compile-fail/pin_data/twice.rs:4:1 | 3 | #[pin_data] - | ^^^^^^^^^^^ - | | - | first implementation here - | conflicting implementation for `Foo` + | ----------- first implementation here +4 | #[pin_data] + | ^^^^^^^^^^^ conflicting implementation for `Foo` | - = note: this error originates in the macro `$crate::__pin_data` which comes from the expansion of the attribute macro `pin_data` (in Nightly builds, run with -Z macro-backtrace for more info) + = 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 `Unpin` for type `Foo` - --> tests/ui/compile-fail/pin_data/twice.rs:3:1 + --> tests/ui/compile-fail/pin_data/twice.rs:4:1 | 3 | #[pin_data] - | ^^^^^^^^^^^ - | | - | first implementation here - | conflicting implementation for `Foo` + | ----------- first implementation here +4 | #[pin_data] + | ^^^^^^^^^^^ conflicting implementation for `Foo` | - = note: this error originates in the macro `$crate::__pin_data` which comes from the expansion of the attribute macro `pin_data` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the attribute macro `pin_data` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0592]: duplicate definitions with name `project` - --> tests/ui/compile-fail/pin_data/twice.rs:3:1 + --> tests/ui/compile-fail/pin_data/twice.rs:4:1 | 3 | #[pin_data] - | ^^^^^^^^^^^ - | | - | duplicate definitions for `project` - | other definition for `project` + | ----------- other definition for `project` +4 | #[pin_data] + | ^^^^^^^^^^^ duplicate definitions for `project` | - = note: this error originates in the macro `$crate::__pin_data` which comes from the expansion of the attribute macro `pin_data` (in Nightly builds, run with -Z macro-backtrace for more info) + = 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 - --> tests/ui/compile-fail/pin_data/twice.rs:3:1 + --> tests/ui/compile-fail/pin_data/twice.rs:4:1 | -3 | #[pin_data] - | ^^^^^^^^^^^ expected `&mut usize`, found `Pin<&mut usize>` +4 | #[pin_data] + | ^^^^^^^^^^^ expected `Pin<&mut usize>`, found `&mut usize` | - = note: expected mutable reference `&mut usize` - found struct `Pin<&mut usize>` - = note: this error originates in the macro `$crate::__pin_data` which comes from the expansion of the attribute macro `pin_data` (in Nightly builds, run with -Z macro-backtrace for more info) + = 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/compile-fail/pin_data/unexpected_args.stderr b/tests/ui/compile-fail/pin_data/unexpected_args.stderr index 4744fc12..9ad20047 100644 --- a/tests/ui/compile-fail/pin_data/unexpected_args.stderr +++ b/tests/ui/compile-fail/pin_data/unexpected_args.stderr @@ -1,15 +1,5 @@ -error: compile_error! takes 1 argument - --> tests/ui/compile-fail/pin_data/unexpected_args.rs:3:1 +error: expected `PinnedDrop` + --> tests/ui/compile-fail/pin_data/unexpected_args.rs:3:12 | 3 | #[pin_data(Bar)] - | ^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `$crate::__pin_data` which comes from the expansion of the attribute macro `pin_data` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: Wrong parameters to `#[pin_data]`, expected nothing or `PinnedDrop`, got '{}'. - --> tests/ui/compile-fail/pin_data/unexpected_args.rs:3:1 - | -3 | #[pin_data(Bar)] - | ^^^^^^^^^^^^^^^^ - | - = note: this error originates in the macro `$crate::__pin_data` which comes from the expansion of 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/unexpected_item.stderr b/tests/ui/compile-fail/pin_data/unexpected_item.stderr index 1772f228..580792ac 100644 --- a/tests/ui/compile-fail/pin_data/unexpected_item.stderr +++ b/tests/ui/compile-fail/pin_data/unexpected_item.stderr @@ -1,16 +1,4 @@ -error: no rules expected keyword `fn` - --> tests/ui/compile-fail/pin_data/unexpected_item.rs:4:1 - | -4 | fn foo() {} - | ^^ no rules expected this token in macro call - | -note: while trying to match keyword `struct` - --> src/macros.rs - | - | $vis:vis struct $name:ident - | ^^^^^^ - -error: Could not locate type name. +error: `#[pin_data]` can only be applied to struct, enum and union defintions --> tests/ui/compile-fail/pin_data/unexpected_item.rs:3:1 | 3 | #[pin_data] diff --git a/tests/ui/compile-fail/pinned_drop/no_fn.stderr b/tests/ui/compile-fail/pinned_drop/no_fn.stderr index fdef4b87..f82b3006 100644 --- a/tests/ui/compile-fail/pinned_drop/no_fn.stderr +++ b/tests/ui/compile-fail/pinned_drop/no_fn.stderr @@ -1,12 +1,7 @@ -error: no rules expected `)` - --> tests/ui/compile-fail/pinned_drop/no_fn.rs:6:1 +error[E0046]: not all trait items implemented, missing: `drop` + --> tests/ui/compile-fail/pinned_drop/no_fn.rs:7:1 | -6 | #[pinned_drop] - | ^^^^^^^^^^^^^^ no rules expected this token in macro call +7 | impl PinnedDrop for Foo {} + | ^^^^^^^^^^^^^^^^^^^^^^^ missing `drop` in implementation | -note: while trying to match keyword `fn` - --> src/macros.rs - | - | fn drop($($sig:tt)*) { - | ^^ - = note: this error originates in the attribute macro `pinned_drop` (in Nightly builds, run with -Z macro-backtrace for more info) + = help: implement the missing item: `fn drop(self: Pin<&mut Self>, _: OnlyCallFromDrop) { todo!() }` diff --git a/tests/ui/compile-fail/pinned_drop/unexpected_additional_item.rs b/tests/ui/compile-fail/pinned_drop/unexpected_additional_item.rs index 851c4cff..d039fde1 100644 --- a/tests/ui/compile-fail/pinned_drop/unexpected_additional_item.rs +++ b/tests/ui/compile-fail/pinned_drop/unexpected_additional_item.rs @@ -1,4 +1,5 @@ use pin_init::*; +use std::pin::Pin; #[pin_data(PinnedDrop)] struct Foo {} diff --git a/tests/ui/compile-fail/pinned_drop/unexpected_additional_item.stderr b/tests/ui/compile-fail/pinned_drop/unexpected_additional_item.stderr index c2cfe925..cf632876 100644 --- a/tests/ui/compile-fail/pinned_drop/unexpected_additional_item.stderr +++ b/tests/ui/compile-fail/pinned_drop/unexpected_additional_item.stderr @@ -1,11 +1,5 @@ -error: no rules expected keyword `const` - --> tests/ui/compile-fail/pinned_drop/unexpected_additional_item.rs:10:5 +error[E0438]: const `BAZ` is not a member of trait `pin_init::PinnedDrop` + --> tests/ui/compile-fail/pinned_drop/unexpected_additional_item.rs:11:5 | -10 | const BAZ: usize = 0; - | ^^^^^ no rules expected this token in macro call - | -note: while trying to match `)` - --> src/macros.rs - | - | ), - | ^ +11 | const BAZ: usize = 0; + | ^^^^^^^^^^^^^^^^^^^^^ not a member of trait `pin_init::PinnedDrop` diff --git a/tests/ui/compile-fail/pinned_drop/unexpected_generics.stderr b/tests/ui/compile-fail/pinned_drop/unexpected_generics.stderr index 194ae81a..cf94d8ea 100644 --- a/tests/ui/compile-fail/pinned_drop/unexpected_generics.stderr +++ b/tests/ui/compile-fail/pinned_drop/unexpected_generics.stderr @@ -1,13 +1,5 @@ -error[E0107]: trait takes 0 generic arguments but 1 generic argument was supplied - --> tests/ui/compile-fail/pinned_drop/unexpected_generics.rs:10:9 +error: unexpected arguments for `PinnedDrop` path + --> tests/ui/compile-fail/pinned_drop/unexpected_generics.rs:10:19 | 10 | impl PinnedDrop for Foo { - | ^^^^^^^^^^--- help: remove the unnecessary generics - | | - | expected 0 generic arguments - | -note: trait defined here, with 0 generic parameters - --> src/lib.rs - | - | pub unsafe trait PinnedDrop: __internal::HasPinData { - | ^^^^^^^^^^ + | ^^^ diff --git a/tests/ui/compile-fail/pinned_drop/unexpected_item.stderr b/tests/ui/compile-fail/pinned_drop/unexpected_item.stderr index 5f63ad8f..6f564a2b 100644 --- a/tests/ui/compile-fail/pinned_drop/unexpected_item.stderr +++ b/tests/ui/compile-fail/pinned_drop/unexpected_item.stderr @@ -1,11 +1,13 @@ -error: no rules expected keyword `const` +error[E0438]: const `BAZ` is not a member of trait `pin_init::PinnedDrop` --> tests/ui/compile-fail/pinned_drop/unexpected_item.rs:8:5 | 8 | const BAZ: usize = 0; - | ^^^^^ no rules expected this token in macro call + | ^^^^^^^^^^^^^^^^^^^^^ not a member of trait `pin_init::PinnedDrop` + +error[E0046]: not all trait items implemented, missing: `drop` + --> tests/ui/compile-fail/pinned_drop/unexpected_item.rs:7:1 | -note: while trying to match keyword `fn` - --> src/macros.rs +7 | impl PinnedDrop for Foo { + | ^^^^^^^^^^^^^^^^^^^^^^^ missing `drop` in implementation | - | fn drop($($sig:tt)*) { - | ^^ + = help: implement the missing item: `fn drop(self: Pin<&mut Self>, _: OnlyCallFromDrop) { todo!() }` diff --git a/tests/ui/compile-fail/pinned_drop/useless_pinned_drop.stderr b/tests/ui/compile-fail/pinned_drop/useless_pinned_drop.stderr index 95c4a757..d54aeb01 100644 --- a/tests/ui/compile-fail/pinned_drop/useless_pinned_drop.stderr +++ b/tests/ui/compile-fail/pinned_drop/useless_pinned_drop.stderr @@ -7,4 +7,4 @@ error[E0119]: conflicting implementations of trait `UselessPinnedDropImpl_you_ne | first implementation here | conflicting implementation for `Foo` | - = note: this error originates in the macro `$crate::__pin_data` which comes from the expansion of the attribute macro `pin_data` (in Nightly builds, run with -Z macro-backtrace for more info) + = 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/zeroable/enum.rs b/tests/ui/compile-fail/zeroable/enum.rs new file mode 100644 index 00000000..2b676fe0 --- /dev/null +++ b/tests/ui/compile-fail/zeroable/enum.rs @@ -0,0 +1,16 @@ +extern crate pin_init; +use pin_init::*; + +#[derive(Zeroable)] +enum Num { + A(u32), + B(i32), +} + +#[derive(MaybeZeroable)] +enum Num2 { + A(u32), + B(i32), +} + +fn main() {} diff --git a/tests/ui/compile-fail/zeroable/enum.stderr b/tests/ui/compile-fail/zeroable/enum.stderr new file mode 100644 index 00000000..bc07203b --- /dev/null +++ b/tests/ui/compile-fail/zeroable/enum.stderr @@ -0,0 +1,11 @@ +error: cannot derive `Zeroable` for an enum + --> tests/ui/compile-fail/zeroable/enum.rs:5:1 + | +5 | enum Num { + | ^^^^ + +error: cannot derive `Zeroable` for an enum + --> tests/ui/compile-fail/zeroable/enum.rs:11:1 + | +11 | enum Num2 { + | ^^^^ diff --git a/tests/ui/compile-fail/zeroable/not_all_fields_zeroable.stderr b/tests/ui/compile-fail/zeroable/not_all_fields_zeroable.stderr index 67b63241..aa54e9c8 100644 --- a/tests/ui/compile-fail/zeroable/not_all_fields_zeroable.stderr +++ b/tests/ui/compile-fail/zeroable/not_all_fields_zeroable.stderr @@ -9,7 +9,7 @@ note: required by a bound in `assert_zeroable` | 4 | #[derive(Zeroable)] | ^^^^^^^^ required by this bound in `assert_zeroable` - = note: this error originates in the macro `::pin_init::__derive_zeroable` which comes from the expansion of the derive macro `Zeroable` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the derive macro `Zeroable` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider removing the leading `&`-reference | 7 - b: &'static Foo, diff --git a/tests/ui/compile-fail/zeroable/with_comma.stderr b/tests/ui/compile-fail/zeroable/with_comma.stderr index ea217242..f172f31f 100644 --- a/tests/ui/compile-fail/zeroable/with_comma.stderr +++ b/tests/ui/compile-fail/zeroable/with_comma.stderr @@ -1,33 +1,5 @@ -error: no rules expected `,` - --> tests/ui/compile-fail/zeroable/with_comma.rs:11:13 +error: unexpected token, expected `}` + --> tests/ui/compile-fail/zeroable/with_comma.rs:13:34 | -11 | let _ = init!(Foo { - | _____________^ -12 | | a: 0, -13 | | ..Zeroable::init_zeroed(), -14 | | }); - | |______^ no rules expected this token in macro call - | -note: while trying to match `)` - --> src/macros.rs - | - | @munch_fields($(..Zeroable::init_zeroed())? $(,)?), - | ^ - = note: this error originates in the macro `$crate::__init_internal` which comes from the expansion of the macro `init` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: no rules expected `,` - --> tests/ui/compile-fail/zeroable/with_comma.rs:11:13 - | -11 | let _ = init!(Foo { - | _____________^ -12 | | a: 0, -13 | | ..Zeroable::init_zeroed(), -14 | | }); - | |______^ no rules expected this token in macro call - | -note: while trying to match `)` - --> src/macros.rs - | - | @munch_fields(..Zeroable::init_zeroed() $(,)?), - | ^ - = note: this error originates in the macro `$crate::__init_internal` which comes from the expansion of the macro `init` (in Nightly builds, run with -Z macro-backtrace for more info) +13 | ..Zeroable::init_zeroed(), + | ^ diff --git a/tests/ui/expand/many_generics.expanded.rs b/tests/ui/expand/many_generics.expanded.rs index 007d4388..44a4efa1 100644 --- a/tests/ui/expand/many_generics.expanded.rs +++ b/tests/ui/expand/many_generics.expanded.rs @@ -1,4 +1,5 @@ #![feature(lint_reasons)] +#![allow(dead_code)] use core::{marker::PhantomPinned, pin::Pin}; use pin_init::*; trait Bar<'a, const ID: usize = 0> { @@ -12,6 +13,8 @@ where r: &'b mut [&'a mut T; SIZE], _pin: PhantomPinned, } +/// Pin-projections of [`Foo`] +#[allow(dead_code)] #[doc(hidden)] struct FooProjection< '__pin, @@ -20,9 +23,9 @@ struct FooProjection< T: Bar<'b> + ?Sized + 'a, const SIZE: usize = 0, > { - _pin: ::core::pin::Pin<&'__pin mut PhantomPinned>, 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> @@ -43,15 +46,16 @@ where ) -> FooProjection<'__pin, 'a, 'b, T, SIZE> { let this = unsafe { ::core::pin::Pin::get_unchecked_mut(self) }; FooProjection { - _pin: unsafe { ::core::pin::Pin::new_unchecked(&mut this._pin) }, 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 _: () = { - struct __ThePinData<'a, 'b: 'a, T: Bar<'b> + ?Sized + 'a, const SIZE: usize> + #[doc(hidden)] + struct __ThePinData<'a, 'b: 'a, T: Bar<'b> + ?Sized + 'a, const SIZE: usize = 0> where T: Bar<'a, 1>, { @@ -84,19 +88,11 @@ const _: () = { where T: Bar<'a, 1>, { - unsafe fn _pin( - self, - slot: *mut PhantomPinned, - init: impl ::pin_init::PinInit, - ) -> ::core::result::Result<(), E> { - unsafe { ::pin_init::PinInit::__pinned_init(init, slot) } - } - unsafe fn __project__pin<'__slot>( - self, - slot: &'__slot mut PhantomPinned, - ) -> ::core::pin::Pin<&'__slot mut PhantomPinned> { - ::core::pin::Pin::new_unchecked(slot) - } + /// # Safety + /// + /// - `slot` is a valid pointer to uninitialized memory. + /// - the caller does not touch `slot` when `Err` is returned, they are only permitted + /// to deallocate. unsafe fn array( self, slot: *mut [u8; 1024 * 1024], @@ -104,12 +100,20 @@ const _: () = { ) -> ::core::result::Result<(), E> { unsafe { ::pin_init::Init::__init(init, slot) } } + /// # Safety + /// + /// `slot` points at the field `array` inside of `Foo`, which is pinned. unsafe fn __project_array<'__slot>( self, slot: &'__slot mut [u8; 1024 * 1024], ) -> &'__slot mut [u8; 1024 * 1024] { slot } + /// # Safety + /// + /// - `slot` is a valid pointer to uninitialized memory. + /// - the caller does not touch `slot` when `Err` is returned, they are only permitted + /// to deallocate. unsafe fn r( self, slot: *mut &'b mut [&'a mut T; SIZE], @@ -117,12 +121,37 @@ const _: () = { ) -> ::core::result::Result<(), E> { unsafe { ::pin_init::Init::__init(init, slot) } } + /// # Safety + /// + /// `slot` points at the field `r` inside of `Foo`, which is pinned. unsafe fn __project_r<'__slot>( self, slot: &'__slot mut &'b mut [&'a mut T; SIZE], ) -> &'__slot mut &'b mut [&'a mut T; SIZE] { slot } + /// # Safety + /// + /// - `slot` is a valid pointer to uninitialized memory. + /// - the caller does not touch `slot` when `Err` is returned, they are only permitted + /// to deallocate. + /// - `slot` will not move until it is dropped, i.e. it will be pinned. + unsafe fn _pin( + self, + slot: *mut PhantomPinned, + init: impl ::pin_init::PinInit, + ) -> ::core::result::Result<(), E> { + unsafe { ::pin_init::PinInit::__pinned_init(init, slot) } + } + /// # Safety + /// + /// `slot` points at the field `_pin` inside of `Foo`, which is pinned. + unsafe fn __project__pin<'__slot>( + self, + slot: &'__slot mut PhantomPinned, + ) -> ::core::pin::Pin<&'__slot mut PhantomPinned> { + unsafe { ::core::pin::Pin::new_unchecked(slot) } + } } unsafe impl< 'a, @@ -152,7 +181,7 @@ const _: () = { type Datee = Foo<'a, 'b, T, SIZE>; } #[allow(dead_code)] - struct __Unpin<'__pin, 'a, 'b: 'a, T: Bar<'b> + ?Sized + 'a, const SIZE: usize> + struct __Unpin<'__pin, 'a, 'b: 'a, T: Bar<'b> + ?Sized + 'a, const SIZE: usize = 0> where T: Bar<'a, 1>, { diff --git a/tests/ui/expand/pin-data.expanded.rs b/tests/ui/expand/pin-data.expanded.rs index 9d563f15..fb59d866 100644 --- a/tests/ui/expand/pin-data.expanded.rs +++ b/tests/ui/expand/pin-data.expanded.rs @@ -4,10 +4,12 @@ struct Foo { array: [u8; 1024 * 1024], _pin: PhantomPinned, } +/// Pin-projections of [`Foo`] +#[allow(dead_code)] #[doc(hidden)] struct FooProjection<'__pin> { - _pin: ::core::pin::Pin<&'__pin mut PhantomPinned>, array: &'__pin mut [u8; 1024 * 1024], + _pin: ::core::pin::Pin<&'__pin mut PhantomPinned>, ___pin_phantom_data: ::core::marker::PhantomData<&'__pin mut ()>, } impl Foo { @@ -24,13 +26,14 @@ impl Foo { ) -> FooProjection<'__pin> { let this = unsafe { ::core::pin::Pin::get_unchecked_mut(self) }; FooProjection { - _pin: unsafe { ::core::pin::Pin::new_unchecked(&mut this._pin) }, 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: ::core::marker::PhantomData Foo>, } @@ -43,19 +46,11 @@ const _: () = { #[allow(dead_code)] #[expect(clippy::missing_safety_doc)] impl __ThePinData { - unsafe fn _pin( - self, - slot: *mut PhantomPinned, - init: impl ::pin_init::PinInit, - ) -> ::core::result::Result<(), E> { - unsafe { ::pin_init::PinInit::__pinned_init(init, slot) } - } - unsafe fn __project__pin<'__slot>( - self, - slot: &'__slot mut PhantomPinned, - ) -> ::core::pin::Pin<&'__slot mut PhantomPinned> { - ::core::pin::Pin::new_unchecked(slot) - } + /// # Safety + /// + /// - `slot` is a valid pointer to uninitialized memory. + /// - the caller does not touch `slot` when `Err` is returned, they are only permitted + /// to deallocate. unsafe fn array( self, slot: *mut [u8; 1024 * 1024], @@ -63,12 +58,37 @@ const _: () = { ) -> ::core::result::Result<(), E> { unsafe { ::pin_init::Init::__init(init, slot) } } + /// # Safety + /// + /// `slot` points at the field `array` inside of `Foo`, which is pinned. unsafe fn __project_array<'__slot>( self, slot: &'__slot mut [u8; 1024 * 1024], ) -> &'__slot mut [u8; 1024 * 1024] { slot } + /// # Safety + /// + /// - `slot` is a valid pointer to uninitialized memory. + /// - the caller does not touch `slot` when `Err` is returned, they are only permitted + /// to deallocate. + /// - `slot` will not move until it is dropped, i.e. it will be pinned. + unsafe fn _pin( + self, + slot: *mut PhantomPinned, + init: impl ::pin_init::PinInit, + ) -> ::core::result::Result<(), E> { + unsafe { ::pin_init::PinInit::__pinned_init(init, slot) } + } + /// # Safety + /// + /// `slot` points at the field `_pin` inside of `Foo`, which is pinned. + unsafe fn __project__pin<'__slot>( + self, + slot: &'__slot mut PhantomPinned, + ) -> ::core::pin::Pin<&'__slot mut PhantomPinned> { + unsafe { ::core::pin::Pin::new_unchecked(slot) } + } } unsafe impl ::pin_init::__internal::HasPinData for Foo { type PinData = __ThePinData; @@ -94,11 +114,12 @@ const _: () = { {} trait MustNotImplDrop {} #[expect(drop_bounds)] - impl MustNotImplDrop for T {} + impl MustNotImplDrop for T {} impl MustNotImplDrop for Foo {} #[expect(non_camel_case_types)] trait UselessPinnedDropImpl_you_need_to_specify_PinnedDrop {} - impl UselessPinnedDropImpl_you_need_to_specify_PinnedDrop - for T {} + impl< + T: ::pin_init::PinnedDrop + ?::core::marker::Sized, + > UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for T {} impl UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for Foo {} }; diff --git a/tests/ui/expand/pinned_drop.expanded.rs b/tests/ui/expand/pinned_drop.expanded.rs index 8aacd750..5e0f1250 100644 --- a/tests/ui/expand/pinned_drop.expanded.rs +++ b/tests/ui/expand/pinned_drop.expanded.rs @@ -4,10 +4,12 @@ struct Foo { array: [u8; 1024 * 1024], _pin: PhantomPinned, } +/// Pin-projections of [`Foo`] +#[allow(dead_code)] #[doc(hidden)] struct FooProjection<'__pin> { - _pin: ::core::pin::Pin<&'__pin mut PhantomPinned>, array: &'__pin mut [u8; 1024 * 1024], + _pin: ::core::pin::Pin<&'__pin mut PhantomPinned>, ___pin_phantom_data: ::core::marker::PhantomData<&'__pin mut ()>, } impl Foo { @@ -24,13 +26,14 @@ impl Foo { ) -> FooProjection<'__pin> { let this = unsafe { ::core::pin::Pin::get_unchecked_mut(self) }; FooProjection { - _pin: unsafe { ::core::pin::Pin::new_unchecked(&mut this._pin) }, 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: ::core::marker::PhantomData Foo>, } @@ -43,19 +46,11 @@ const _: () = { #[allow(dead_code)] #[expect(clippy::missing_safety_doc)] impl __ThePinData { - unsafe fn _pin( - self, - slot: *mut PhantomPinned, - init: impl ::pin_init::PinInit, - ) -> ::core::result::Result<(), E> { - unsafe { ::pin_init::PinInit::__pinned_init(init, slot) } - } - unsafe fn __project__pin<'__slot>( - self, - slot: &'__slot mut PhantomPinned, - ) -> ::core::pin::Pin<&'__slot mut PhantomPinned> { - ::core::pin::Pin::new_unchecked(slot) - } + /// # Safety + /// + /// - `slot` is a valid pointer to uninitialized memory. + /// - the caller does not touch `slot` when `Err` is returned, they are only permitted + /// to deallocate. unsafe fn array( self, slot: *mut [u8; 1024 * 1024], @@ -63,12 +58,37 @@ const _: () = { ) -> ::core::result::Result<(), E> { unsafe { ::pin_init::Init::__init(init, slot) } } + /// # Safety + /// + /// `slot` points at the field `array` inside of `Foo`, which is pinned. unsafe fn __project_array<'__slot>( self, slot: &'__slot mut [u8; 1024 * 1024], ) -> &'__slot mut [u8; 1024 * 1024] { slot } + /// # Safety + /// + /// - `slot` is a valid pointer to uninitialized memory. + /// - the caller does not touch `slot` when `Err` is returned, they are only permitted + /// to deallocate. + /// - `slot` will not move until it is dropped, i.e. it will be pinned. + unsafe fn _pin( + self, + slot: *mut PhantomPinned, + init: impl ::pin_init::PinInit, + ) -> ::core::result::Result<(), E> { + unsafe { ::pin_init::PinInit::__pinned_init(init, slot) } + } + /// # Safety + /// + /// `slot` points at the field `_pin` inside of `Foo`, which is pinned. + unsafe fn __project__pin<'__slot>( + self, + slot: &'__slot mut PhantomPinned, + ) -> ::core::pin::Pin<&'__slot mut PhantomPinned> { + unsafe { ::core::pin::Pin::new_unchecked(slot) } + } } unsafe impl ::pin_init::__internal::HasPinData for Foo { type PinData = __ThePinData; diff --git a/tests/ui/expand/simple-init.expanded.rs b/tests/ui/expand/simple-init.expanded.rs index 320efc95..c050a449 100644 --- a/tests/ui/expand/simple-init.expanded.rs +++ b/tests/ui/expand/simple-init.expanded.rs @@ -3,7 +3,7 @@ struct Foo {} fn main() { let _ = { struct __InitOk; - let data = unsafe { + let __data = unsafe { use ::pin_init::__internal::HasInitData; Foo::__init_data() }; @@ -12,16 +12,12 @@ fn main() { __InitOk, ::core::convert::Infallible, >( - data, + __data, move |slot| { { struct __InitOk; #[allow(unreachable_code, clippy::diverging_sub_expression)] - let _ = || { - unsafe { - ::core::ptr::write(slot, Foo {}); - }; - }; + let _ = || unsafe { ::core::ptr::write(slot, Foo {}) }; } Ok(__InitOk) }, diff --git a/tests/ui/expand/zeroable.expanded.rs b/tests/ui/expand/zeroable.expanded.rs index 4f9d174c..08ad36fa 100644 --- a/tests/ui/expand/zeroable.expanded.rs +++ b/tests/ui/expand/zeroable.expanded.rs @@ -31,11 +31,11 @@ struct WithGenerics<'a, T, U: Trait> { unsafe impl< 'a, T: ::pin_init::Zeroable, - U: ::pin_init::Zeroable + Trait, + U: Trait + ::pin_init::Zeroable, > ::pin_init::Zeroable for WithGenerics<'a, T, U> {} const _: () = { fn assert_zeroable() {} - fn ensure_zeroable<'a, T: ::pin_init::Zeroable, U: ::pin_init::Zeroable + Trait>() { + fn ensure_zeroable<'a, T: ::pin_init::Zeroable, U: Trait + ::pin_init::Zeroable>() { assert_zeroable::(); assert_zeroable::<&'a U>(); } diff --git a/tests/underscore.rs b/tests/underscore.rs index 583b3b39..a55a238a 100644 --- a/tests/underscore.rs +++ b/tests/underscore.rs @@ -1,4 +1,4 @@ -use pin_init::{try_init, Init}; +use pin_init::{init, Init}; pub struct Foo { x: u64, @@ -12,9 +12,13 @@ fn bar() -> bool { true } +fn baz() -> Result<(), ()> { + Err(()) +} + impl Foo { pub fn new() -> impl Init { - try_init!(Self { + init!(Self { _: { if foo() { return Err(()); @@ -28,4 +32,11 @@ impl Foo { } }? ()) } + + pub fn create(x: u64) -> impl Init { + init!(Self { + _: { baz()? }, + x, + }? ()) + } }