From ea82b33947f60c511dac6eb3815b6af3f6c3f555 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Fri, 8 Dec 2023 15:10:30 +0000 Subject: [PATCH] Add rough `destruct_into_heads` function --- examples/src/ok_tests.rs | 35 ++++++- ouroboros/src/lib.rs | 2 +- ouroboros_macro/src/generate/into_heads.rs | 110 ++++++++++++++++++++- ouroboros_macro/src/generate/with_mut.rs | 2 +- ouroboros_macro/src/info_structures.rs | 2 +- ouroboros_macro/src/lib.rs | 7 +- 6 files changed, 150 insertions(+), 8 deletions(-) diff --git a/examples/src/ok_tests.rs b/examples/src/ok_tests.rs index 1218671..2be6983 100644 --- a/examples/src/ok_tests.rs +++ b/examples/src/ok_tests.rs @@ -1,4 +1,4 @@ -use alloc::{boxed::Box, borrow::ToOwned}; +use alloc::{borrow::ToOwned, boxed::Box}; use core::fmt::Debug; use ouroboros::self_referencing; @@ -273,6 +273,39 @@ fn double_lifetime() { } } +mod destruct_tests { + use ouroboros::self_referencing; + + struct Wrapper<'a> { + int: &'a i32, + } + + impl<'a> Wrapper<'a> { + fn into_int(self) -> &'a i32 { + self.int + } + } + + #[self_referencing] + struct DestructIntoHeads { + data: i32, + #[borrows(data)] + #[covariant] + dref: Wrapper<'this>, + } + + #[test] + fn destruct_into_heads() { + let s = DestructIntoHeads::new(1, |int| Wrapper { int }); + + let (_, heads) = s.destruct_into_heads(|tails| { + assert_eq!(*tails.dref.into_int(), 1); + }); + + assert_eq!(heads.data, 1); + } +} + #[cfg(not(feature = "miri"))] #[rustversion::stable(1.62)] mod compile_tests { diff --git a/ouroboros/src/lib.rs b/ouroboros/src/lib.rs index 722dc69..5e81612 100644 --- a/ouroboros/src/lib.rs +++ b/ouroboros/src/lib.rs @@ -351,8 +351,8 @@ pub mod macro_help { pub extern crate alloc; pub use aliasable::boxed::AliasableBox; - pub use static_assertions::assert_impl_all; use aliasable::boxed::UniqueBox; + pub use static_assertions::assert_impl_all; pub struct CheckIfTypeIsStd(core::marker::PhantomData); diff --git a/ouroboros_macro/src/generate/into_heads.rs b/ouroboros_macro/src/generate/into_heads.rs index 47ad372..29a693c 100644 --- a/ouroboros_macro/src/generate/into_heads.rs +++ b/ouroboros_macro/src/generate/into_heads.rs @@ -1,7 +1,8 @@ -use proc_macro2::TokenStream; +use proc_macro2::{Span, TokenStream}; use quote::quote; +use syn::{Error, Lifetime, WhereClause}; -use crate::info_structures::{Options, StructInfo}; +use crate::info_structures::{FieldType, Options, StructInfo}; /// Returns the Heads struct and a function to convert the original struct into a Heads instance. pub fn make_into_heads(info: &StructInfo, options: Options) -> (TokenStream, TokenStream) { @@ -83,3 +84,108 @@ pub fn make_into_heads(info: &StructInfo, options: Options) -> (TokenStream, Tok }; (heads_struct_def, into_heads_fn) } + +pub fn destruct_into_heads( + info: &StructInfo, + options: Options, +) -> Result<(TokenStream, TokenStream), Error> { + let internal_struct = &info.internal_ident; + + let visibility = if options.do_pub_extras { + info.vis.clone() + } else { + syn::parse_quote! { pub(super) } + }; + let mut code = Vec::new(); + let mut fields = Vec::new(); + let mut field_assignments = Vec::new(); + let mut field_initializers = Vec::new(); + + // I don't think the reverse is necessary but it does make the expanded code more uniform. + for field in info.fields.iter().rev() { + let field_name = &field.name; + let field_type = &field.typ; + if !field.self_referencing { + code.push(quote! { let #field_name = this.#field_name; }); + if field.is_borrowed() { + field_initializers + .push(quote! { #field_name: ::ouroboros::macro_help::unbox(#field_name) }); + } else { + field_initializers.push(quote! { #field_name }); + } + } else if field.field_type == FieldType::Tail { + fields.push(quote! { #visibility #field_name: #field_type }); + field_assignments.push(quote! { #field_name: this.#field_name }); + } + } + + for (ty, ident) in info.generic_consumers() { + fields.push(quote! { #ident: ::core::marker::PhantomData<#ty> }); + field_assignments.push(quote! { #ident: ::core::marker::PhantomData }); + field_initializers.push(quote! { #ident: ::core::marker::PhantomData }); + } + + let new_generic_params = if info.generic_params().is_empty() { + quote! { <'this> } + } else { + let mut new_generic_params = info.generic_params().clone(); + new_generic_params.insert(0, syn::parse_quote! { 'this }); + quote! { <#new_generic_params> } + }; + let generic_args = info.generic_arguments(); + let mut new_generic_args = info.generic_arguments(); + new_generic_args.insert(0, quote! { 'this }); + + let struct_documentation = format!( + concat!( + "A struct for holding ", + "[tail fields](https://docs.rs/ouroboros/latest/ouroboros/attr.self_referencing.html#definitions) in an instance of ", + "[`{0}`]({0})." + ), + info.ident.to_string() + ); + let generic_where = info.generics.where_clause.clone(); + let struct_defs = quote! { + #[doc=#struct_documentation] + #visibility struct OwnedTailFields #new_generic_params #generic_where { #(#fields),* } + }; + let borrowed_fields_type = quote! { OwnedTailFields<#(#new_generic_args),*> }; + let documentation = concat!( + "This method provides immutable references to all ", + "[tail and immutably borrowed fields](https://docs.rs/ouroboros/latest/ouroboros/attr.self_referencing.html#definitions).", + ); + let documentation = if !options.do_no_doc { + quote! { + #[doc=#documentation] + } + } else { + quote! { #[doc(hidden)] } + }; + let fn_defs = quote! { + #documentation + #[inline(always)] + #visibility fn destruct_into_heads ( + self, + user: impl for<'this> ::core::ops::FnOnce(#borrowed_fields_type) -> ReturnType + ) -> (ReturnType, Heads<#(#generic_args),*>) { + let this_ptr = &self as *const _; + let this: #internal_struct<#(#generic_args),*> = unsafe { ::core::mem::transmute_copy(&*this_ptr) }; + ::core::mem::forget(self); + + let user_return = user(OwnedTailFields { + #(#field_assignments),* + }); + + let heads = { + #(#code)* + + Heads { + #(#field_initializers),* + } + }; + + (user_return, heads) + } + }; + Ok((struct_defs, fn_defs)) +} diff --git a/ouroboros_macro/src/generate/with_mut.rs b/ouroboros_macro/src/generate/with_mut.rs index be5587f..6505b02 100644 --- a/ouroboros_macro/src/generate/with_mut.rs +++ b/ouroboros_macro/src/generate/with_mut.rs @@ -24,7 +24,7 @@ pub fn make_with_all_mut_function( let field_name = &field.name; let field_type = &field.typ; let lifetime = format_ident!("this{}", lifetime_idents.len()); - if uses_this_lifetime(quote! { #field_type }) || field.field_type == FieldType::Borrowed { + if uses_this_lifetime(quote! { #field_type }) || field.field_type == FieldType::Borrowed { lifetime_idents.push(lifetime.clone()); } let field_type = replace_this_with_lifetime(quote! { #field_type }, lifetime.clone()); diff --git a/ouroboros_macro/src/info_structures.rs b/ouroboros_macro/src/info_structures.rs index 0a45a2e..8ead3e3 100644 --- a/ouroboros_macro/src/info_structures.rs +++ b/ouroboros_macro/src/info_structures.rs @@ -3,7 +3,7 @@ use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote, ToTokens}; use syn::{ punctuated::Punctuated, token::Comma, Attribute, ConstParam, Error, GenericParam, Generics, - LifetimeParam, Type, TypeParam, Visibility, + LifetimeParam, Type, TypeParam, Visibility, }; #[derive(Clone, Copy)] diff --git a/ouroboros_macro/src/lib.rs b/ouroboros_macro/src/lib.rs index 2589043..c062826 100644 --- a/ouroboros_macro/src/lib.rs +++ b/ouroboros_macro/src/lib.rs @@ -9,8 +9,8 @@ mod utils; use crate::{ generate::{ constructor::create_builder_and_constructor, derives::create_derives, - into_heads::make_into_heads, struc::create_internal_struct_def, - summon_checker::generate_checker_summoner, + into_heads::destruct_into_heads, into_heads::make_into_heads, + struc::create_internal_struct_def, summon_checker::generate_checker_summoner, try_constructor::create_try_builder_and_constructor, type_asserts::make_type_asserts, with::make_with_all_function, with_each::make_with_functions, }, @@ -66,6 +66,7 @@ fn self_referencing_impl( let (with_all_mut_struct_def, with_all_mut_fn_def) = make_with_all_mut_function(&info, options)?; let (heads_struct_def, into_heads_fn) = make_into_heads(&info, options); + let (destruct_heads_struct_def, destruct_into_heads_fn) = destruct_into_heads(&info, options)?; let impls = create_derives(&info)?; @@ -100,6 +101,7 @@ fn self_referencing_impl( #with_all_struct_def #with_all_mut_struct_def #heads_struct_def + #destruct_heads_struct_def #impls impl <#generic_params> #struct_name <#(#generic_args),*> #generic_where { #constructor_def @@ -112,6 +114,7 @@ fn self_referencing_impl( #with_all_fn_def #with_all_mut_fn_def #into_heads_fn + #destruct_into_heads_fn } #type_asserts_def }