Skip to content

Commit

Permalink
Revert "Revert "Failed attempt to fix problems.""
Browse files Browse the repository at this point in the history
This reverts commit b4d49cd.
  • Loading branch information
someguynamedjosh committed May 14, 2023
1 parent 13b2af8 commit c889114
Show file tree
Hide file tree
Showing 14 changed files with 97 additions and 27 deletions.
4 changes: 2 additions & 2 deletions examples/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "ouroboros_examples"
version = "0.15.7"
version = "0.16.0"
authors = ["Joshua Maros <[email protected]>"]
edition = "2018"
license = "MIT OR Apache-2.0"
Expand All @@ -21,7 +21,7 @@ std = []
__tokio = ["tokio", "std"]

[dependencies]
ouroboros = { version = "0.15.7", path = "../ouroboros" }
ouroboros = { version = "0.16.0", path = "../ouroboros" }
tokio = { version = "1.27.0", features = [ "macros", "rt" ], optional = true }

[dev-dependencies]
Expand Down
3 changes: 3 additions & 0 deletions examples/src/ok_tests.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use alloc::borrow::ToOwned;
use alloc::boxed::Box;
use alloc::vec;
use alloc::vec::Vec;
use core::fmt::Debug;
use ouroboros::macro_help::AliasableBox;

use ouroboros::self_referencing;

Expand Down
5 changes: 3 additions & 2 deletions ouroboros/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "ouroboros"
version = "0.15.7"
version = "0.16.0"
authors = ["Joshua Maros <[email protected]>"]
edition = "2018"
license = "MIT OR Apache-2.0"
Expand All @@ -11,7 +11,8 @@ repository = "https://github.com/joshua-maros/ouroboros"

[dependencies]
aliasable = "0.1.3"
ouroboros_macro = { version = "0.15.7", path = "../ouroboros_macro" }
ouroboros_macro = { version = "0.16.0", path = "../ouroboros_macro" }
static_assertions = "1.1.0"

[features]
default = ["std"]
Expand Down
1 change: 1 addition & 0 deletions ouroboros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,7 @@ pub mod macro_help {
pub extern crate alloc;

pub use aliasable::boxed::AliasableBox;
pub use static_assertions::const_assert_eq;
use aliasable::boxed::UniqueBox;

pub struct CheckIfTypeIsStd<T>(core::marker::PhantomData<T>);
Expand Down
2 changes: 1 addition & 1 deletion ouroboros_macro/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "ouroboros_macro"
version = "0.15.7"
version = "0.16.0"
authors = ["Joshua Maros <[email protected]>"]
edition = "2018"
license = "MIT OR Apache-2.0"
Expand Down
15 changes: 13 additions & 2 deletions ouroboros_macro/src/generate/constructor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,12 +154,23 @@ pub fn create_builder_and_constructor(
BuilderType::Sync => quote! { fn new },
};
let field_names: Vec<_> = info.fields.iter().map(|field| field.name.clone()).collect();
let internal_ident = &info.internal_ident;
let constructor_def = quote! {
#documentation
#vis #constructor_fn(#(#params),*) -> #struct_name <#(#generic_args),*> {
::ouroboros::macro_help::const_assert_eq!(
::core::mem::size_of::<#struct_name<#(#generic_args),*>>(),
::core::mem::size_of::<#internal_ident<#(#generic_args),*>>()
);
::ouroboros::macro_help::const_assert_eq!(
::core::mem::align_of::<#struct_name<#(#generic_args),*>>(),
::core::mem::align_of::<#internal_ident<#(#generic_args),*>>()
);
#(#code)*
Self {
#(#field_names),*
unsafe {
::core::mem::transmute(#internal_ident<#(#generic_args),*> {
#(#field_names),*
})
}
}
};
Expand Down
14 changes: 8 additions & 6 deletions ouroboros_macro/src/generate/into_heads.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use proc_macro2::TokenStream;
use quote::quote;
use quote::{format_ident, quote};

use crate::info_structures::{Options, StructInfo};

Expand All @@ -13,12 +13,16 @@ pub fn make_into_heads(info: &StructInfo, options: Options) -> (TokenStream, Tok
let mut code = Vec::new();
let mut field_initializers = Vec::new();
let mut head_fields = Vec::new();
let internal_struct = &info.internal_ident;
// Drop everything in the reverse order of what it was declared in. Fields that come later
// are only dependent on fields that came before them.
for field in info.fields.iter().rev() {
let field_name = &field.name;
if !field.self_referencing {
code.push(quote! { let #field_name = self.#field_name; });
if field.self_referencing {
// Heads are fields that do not borrow anything.
code.push(quote! { ::core::mem::drop(this.#field_name); });
} else {
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) });
Expand All @@ -27,9 +31,6 @@ pub fn make_into_heads(info: &StructInfo, options: Options) -> (TokenStream, Tok
}
let field_type = &field.typ;
head_fields.push(quote! { #visibility #field_name: #field_type });
} else {
// Heads are fields that do not borrow anything.
code.push(quote! { ::core::mem::drop(self.#field_name); });
}
}
for (ty, ident) in info.generic_consumers() {
Expand Down Expand Up @@ -71,6 +72,7 @@ pub fn make_into_heads(info: &StructInfo, options: Options) -> (TokenStream, Tok
#[allow(clippy::drop_copy)]
#[allow(clippy::drop_non_drop)]
#visibility fn into_heads(self) -> Heads<#(#generic_args),*> {
let this: #internal_struct<#(#generic_args),*> = unsafe { ::core::mem::transmute(self) };
#(#code)*
Heads {
#(#field_initializers),*
Expand Down
31 changes: 27 additions & 4 deletions ouroboros_macro/src/generate/struc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,35 @@ use proc_macro2::TokenStream;
use quote::quote;
use syn::Error;

/// Creates the struct that will actually store the data. This involves properly organizing the
/// fields, collecting metadata about them, reversing the order everything is stored in, and
/// converting any uses of 'this to 'static.
/// Creates the struct that will actually store the data.
pub fn create_actual_struct_def(info: &StructInfo) -> Result<TokenStream, Error> {
let vis = utils::submodule_contents_visiblity(&info.vis);
let visibility = utils::submodule_contents_visiblity(&info.vis);
let mut fields = Vec::new();
for (ty, ident) in info.generic_consumers() {
fields.push(quote! { #ident: ::core::marker::PhantomData<#ty> });
}
let generic_params = info.generic_params();
let generic_args = info.generic_arguments();
let generic_where = &info.generics.where_clause;
let ident = &info.ident;
let internal_ident = &info.internal_ident;
Ok(quote! {
#visibility struct #ident <#generic_params> #generic_where {
actual_data: [u8; ::core::mem::size_of::<#internal_ident<#(#generic_args),*>>()],
_alignment: [#internal_ident<#(#generic_args),*>; 0],
}
})
}

/// Creates a struct with fields like the original struct. Instances of the
/// "actual" struct are reinterpreted as instances of the "internal" struct
/// whenever data needs to be accessed. (This gets around the problem that
/// references passed to functions must be valid through the entire function,
/// but references *created* inside a function can be considered invalid
/// whenever, even during the duration of the function.)
pub fn create_internal_struct_def(info: &StructInfo) -> Result<TokenStream, Error> {
let vis = quote! { pub(super) };
let ident = &info.internal_ident;
let generics = &info.generics;

let field_defs: Vec<_> = info
Expand Down
5 changes: 4 additions & 1 deletion ouroboros_macro/src/generate/try_constructor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ pub fn create_try_builder_and_constructor(
quote! { #struct_name::#or_recover_ident(#(#builder_struct_field_names),*).map_err(|(error, _heads)| error) }
};
let field_names: Vec<_> = info.fields.iter().map(|field| field.name.clone()).collect();
let internal_ident = &info.internal_ident;
let constructor_def = quote! {
#documentation
#visibility #constructor_fn<Error_>(#(#params),*) -> ::core::result::Result<#struct_name <#(#generic_args),*>, Error_> {
Expand All @@ -231,7 +232,9 @@ pub fn create_try_builder_and_constructor(
#or_recover_documentation
#visibility #or_recover_constructor_fn<Error_>(#(#params),*) -> ::core::result::Result<#struct_name <#(#generic_args),*>, (Error_, Heads<#(#generic_args),*>)> {
#(#or_recover_code)*
::core::result::Result::Ok(Self { #(#field_names),* })
::core::result::Result::Ok(unsafe {
::core::mem::transmute(#internal_ident { #(#field_names),* })
})
}
};
builder_struct_generic_producers.push(quote! { Error_ });
Expand Down
10 changes: 7 additions & 3 deletions ouroboros_macro/src/generate/with_all.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,20 @@ pub fn make_with_all_function(
let mut field_assignments = Vec::new();
let mut mut_fields = Vec::new();
let mut mut_field_assignments = Vec::new();
let internal_struct = &info.internal_ident;
// 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.field_type == FieldType::Tail {
fields.push(quote! { #visibility #field_name: &'outer_borrow #field_type });
field_assignments.push(quote! { #field_name: &self.#field_name });
field_assignments.push(quote! { #field_name: &this.#field_name });
mut_fields.push(quote! { #visibility #field_name: &'outer_borrow mut #field_type });
mut_field_assignments.push(quote! { #field_name: &mut self.#field_name });
mut_field_assignments.push(quote! { #field_name: &mut this.#field_name });
} else if field.field_type == FieldType::Borrowed {
let ass = quote! { #field_name: unsafe {
::ouroboros::macro_help::change_lifetime(
&*self.#field_name
&*this.#field_name
)
} };
fields.push(quote! { #visibility #field_name: &'this #field_type });
Expand Down Expand Up @@ -108,13 +109,15 @@ pub fn make_with_all_function(
} else {
quote! { #[doc(hidden)] }
};
let generic_args = info.generic_arguments();
let fn_defs = quote! {
#documentation
#[inline(always)]
#visibility fn with <'outer_borrow, ReturnType>(
&'outer_borrow self,
user: impl for<'this> ::core::ops::FnOnce(#borrowed_fields_type) -> ReturnType
) -> ReturnType {
let this: &#internal_struct<#(#generic_args),*> = unsafe { ::core::mem::transmute(self) };
user(BorrowedFields {
#(#field_assignments),*
})
Expand All @@ -125,6 +128,7 @@ pub fn make_with_all_function(
&'outer_borrow mut self,
user: impl for<'this> ::core::ops::FnOnce(#borrowed_mut_fields_type) -> ReturnType
) -> ReturnType {
let this: &mut #internal_struct<#(#generic_args),*> = unsafe { ::core::mem::transmute(self) };
user(BorrowedMutFields {
#(#mut_field_assignments),*
})
Expand Down
17 changes: 12 additions & 5 deletions ouroboros_macro/src/generate/with_each.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ use syn::Error;

pub fn make_with_functions(info: &StructInfo, options: Options) -> Result<Vec<TokenStream>, Error> {
let mut users = Vec::new();
let internal_struct = &info.internal_ident;
let generic_args = info.generic_arguments();
for field in &info.fields {
let visibility = &field.vis;
let field_name = &field.name;
Expand Down Expand Up @@ -34,7 +36,8 @@ pub fn make_with_functions(info: &StructInfo, options: Options) -> Result<Vec<To
&'outer_borrow self,
user: impl for<'this> ::core::ops::FnOnce(&'outer_borrow #field_type) -> ReturnType,
) -> ReturnType {
user(&self. #field_name)
let this: &#internal_struct<#(#generic_args),*> = unsafe { ::core::mem::transmute(self) };
user(&this. #field_name)
}
});
if field.covariant == Some(true) {
Expand All @@ -45,7 +48,8 @@ pub fn make_with_functions(info: &StructInfo, options: Options) -> Result<Vec<To
#visibility fn #borrower_name<'this>(
&'this self,
) -> &'this #field_type {
&self.#field_name
let this: &#internal_struct<#(#generic_args),*> = unsafe { ::core::mem::transmute(self) };
&this.#field_name
}
});
} else if field.covariant.is_none() {
Expand Down Expand Up @@ -76,7 +80,8 @@ pub fn make_with_functions(info: &StructInfo, options: Options) -> Result<Vec<To
&'outer_borrow mut self,
user: impl for<'this> ::core::ops::FnOnce(&'outer_borrow mut #field_type) -> ReturnType,
) -> ReturnType {
user(&mut self. #field_name)
let this: &mut #internal_struct<#(#generic_args),*> = unsafe { ::core::mem::transmute(self) };
user(&mut this. #field_name)
}
});
} else if field.field_type == FieldType::Borrowed {
Expand All @@ -102,7 +107,8 @@ pub fn make_with_functions(info: &StructInfo, options: Options) -> Result<Vec<To
&'outer_borrow self,
user: impl for<'this> ::core::ops::FnOnce(&'outer_borrow #field_type) -> ReturnType,
) -> ReturnType {
user(&*self.#field_name)
let this: &#internal_struct<#(#generic_args),*> = unsafe { ::core::mem::transmute(self) };
user(&*this.#field_name)
}
});
if field.self_referencing {
Expand All @@ -120,7 +126,8 @@ pub fn make_with_functions(info: &StructInfo, options: Options) -> Result<Vec<To
#visibility fn #borrower_name<'this>(
&'this self,
) -> &'this #field_type {
&*self.#field_name
let this: &#internal_struct<#(#generic_args),*> = unsafe { ::core::mem::transmute(self) };
&*this.#field_name
}
});
} else if field.field_type == FieldType::BorrowedMut {
Expand Down
11 changes: 11 additions & 0 deletions ouroboros_macro/src/info_structures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@ pub struct Options {
pub do_pub_extras: bool,
}

impl Options {
pub fn documentation_to_tokens(&self, documentation: &str) -> TokenStream {
if self.do_no_doc {
quote! { #[doc(hidden)] }
} else {
quote! { #[doc=#documentation] }
}
}
}

#[derive(Clone, Copy, PartialEq)]
pub enum FieldType {
/// Not borrowed by other parts of the struct.
Expand Down Expand Up @@ -61,6 +71,7 @@ impl BuilderType {
pub struct StructInfo {
pub derives: Vec<Derive>,
pub ident: Ident,
pub internal_ident: Ident,
pub generics: Generics,
pub vis: Visibility,
pub fields: Vec<StructFieldInfo>,
Expand Down
5 changes: 4 additions & 1 deletion ouroboros_macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ mod utils;
use crate::{
generate::{
constructor::create_builder_and_constructor, derives::create_derives,
into_heads::make_into_heads, struc::create_actual_struct_def,
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_all::make_with_all_function, with_each::make_with_functions,
},
info_structures::Options,
parse::parse_struct,
};
use generate::struc::create_actual_struct_def;
use inflector::Inflector;
use info_structures::BuilderType;
use proc_macro::TokenStream;
Expand All @@ -37,6 +38,7 @@ fn self_referencing_impl(
let info = parse_struct(original_struct_def)?;

let actual_struct_def = create_actual_struct_def(&info)?;
let internal_struct_def = create_internal_struct_def(&info)?;

let borrowchk_summoner = generate_checker_summoner(&info)?;

Expand Down Expand Up @@ -78,6 +80,7 @@ fn self_referencing_impl(
use super::*;
#[doc="The self-referencing struct."]
#actual_struct_def
#internal_struct_def
#borrowchk_summoner
#builder_def
#async_builder_def
Expand Down
1 change: 1 addition & 0 deletions ouroboros_macro/src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ pub fn parse_struct(def: &ItemStruct) -> Result<StructInfo, Error> {
return Ok(StructInfo {
derives,
ident: def.ident.clone(),
internal_ident: format_ident!("{}Internal", def.ident),
generics: def.generics.clone(),
fields,
vis,
Expand Down

0 comments on commit c889114

Please sign in to comment.