Skip to content

Commit

Permalink
Add borrowchk summoner
Browse files Browse the repository at this point in the history
  • Loading branch information
someguynamedjosh committed Oct 10, 2021
1 parent 59cdb9c commit 9c4db7b
Show file tree
Hide file tree
Showing 10 changed files with 112 additions and 36 deletions.
16 changes: 9 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@
Easy self-referential struct generation for Rust.
Dual licensed under MIT / Apache 2.0.

Note: Version `0.11.0` and later place restrictions on derive macros, earlier
versions allowed using them in ways which could lead to undefined behavior if
not used properly.

Note: Version `0.10.0` and later automatically box every field. This is done
to prevent undefined behavior, but has the side effect of making the library
easier to work with.
Version notes:
- Version `0.13.0` and later contain checks for additional situations which
cause undefined behavior if not caught.
- Version `0.11.0` and later place restrictions on derive macros, earlier
versions allowed using them in ways which could lead to undefined behavior if
not used properly.
- Version `0.10.0` and later automatically box every field. This is done
to prevent undefined behavior, but has the side effect of making the library
easier to work with.

Tests are located in the examples/ folder because they need to be in a crate
outside of `ouroboros` for the `self_referencing` macro to work properly.
Expand Down
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.12.0"
version = "0.13.0"
authors = ["Joshua Maros <[email protected]>"]
edition = "2018"
license = "MIT OR Apache-2.0"
Expand All @@ -19,7 +19,7 @@ default = []
miri = []

[dependencies]
ouroboros = { version = "0.12.0", path = "../ouroboros" }
ouroboros = { version = "0.13.0", path = "../ouroboros" }
stable_deref_trait = "1.2"

[dev-dependencies]
Expand Down
4 changes: 2 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.12.0"
version = "0.13.0"
authors = ["Joshua Maros <[email protected]>"]
edition = "2018"
license = "MIT OR Apache-2.0"
Expand All @@ -11,5 +11,5 @@ repository = "https://github.com/joshua-maros/ouroboros"

[dependencies]
aliasable = "0.1.3"
ouroboros_macro = { version = "0.12.0", path = "../ouroboros_macro" }
ouroboros_macro = { version = "0.13.0", path = "../ouroboros_macro" }
stable_deref_trait = "1.2"
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.12.0"
version = "0.13.0"
authors = ["Joshua Maros <[email protected]>"]
edition = "2018"
license = "MIT OR Apache-2.0"
Expand Down
3 changes: 0 additions & 3 deletions ouroboros_macro/src/generate/constructor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,6 @@ pub fn create_builder_and_constructor(
// it work.
let builder_name = field.builder_name();
params.push(quote! { #builder_name : impl #bound_type });
// Ok so hear me out basically without this thing here my IDE thinks the rest of the
// code is a string and it all turns green.
{}
doc_table += &format!(
"| `{}` | Use a function or closure: `(",
builder_name.to_string()
Expand Down
1 change: 1 addition & 0 deletions ouroboros_macro/src/generate/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pub mod constructor;
pub mod derives;
pub mod into_heads;
pub mod struc;
pub mod summon_borrowchk;
pub mod try_constructor;
pub mod type_asserts;
pub mod with_all;
Expand Down
55 changes: 55 additions & 0 deletions ouroboros_macro/src/generate/summon_borrowchk.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
use proc_macro2::TokenStream;
use quote::quote;
use syn::Error;

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

pub fn generate_borrowchk_summoner(info: &StructInfo) -> Result<TokenStream, Error> {
let mut code: Vec<TokenStream> = Vec::new();
let mut params: Vec<TokenStream> = Vec::new();
let mut value_consumers: Vec<TokenStream> = Vec::new();
let mut template_consumers: Vec<TokenStream> = Vec::new();
for field in &info.fields {
let field_name = &field.name;

let arg_type = field.make_constructor_arg_type(&info, false)?;
if let ArgType::Plain(plain_type) = arg_type {
// No fancy builder function, we can just move the value directly into the struct.
params.push(quote! { #field_name: #plain_type });
} else if let ArgType::TraitBound(bound_type) = arg_type {
// Trait bounds are much trickier. We need a special syntax to accept them in the
// contructor, and generic parameters need to be added to the builder struct to make
// it work.
let builder_name = field.builder_name();
params.push(quote! { #builder_name : impl #bound_type });
let mut builder_args = Vec::new();
for (_, borrow) in field.borrows.iter().enumerate() {
let borrowed_name = &info.fields[borrow.index].name;
builder_args.push(quote! { &#borrowed_name });
}
code.push(quote! { let #field_name = #builder_name (#(#builder_args),*); });
}
if field.is_borrowed() {
let boxed = quote! { ::ouroboros::macro_help::aliasable_boxed(#field_name) };
code.push(quote! { let #field_name = #boxed; });
};
if !field.is_mutably_borrowed() {
value_consumers.push(quote! { #field_name: &#field_name });
}
}
for (_ty, ident) in info.generic_consumers() {
template_consumers.push(quote! { #ident: ::core::marker::PhantomData });
}
let generic_params = info.generic_params();
Ok(quote! {
fn check_if_okay_according_to_borrow_checker<#generic_params>(
#(#params,)*
) {
#(#code;)*
BorrowedFields {
#(#value_consumers,)*
#(#template_consumers,)*
};
}
})
}
28 changes: 8 additions & 20 deletions ouroboros_macro/src/generate/with_all.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,26 +41,14 @@ pub fn make_with_all_function(
}
}

let new_generic_params = if info.generic_params().is_empty() {
quote! { <'outer_borrow, 'this> }
} else {
for (ty, ident) in info.generic_consumers() {
fields.push(quote! { #ident: ::core::marker::PhantomData<#ty> });
mut_fields.push(quote! { #ident: ::core::marker::PhantomData<#ty> });
field_assignments.push(quote! { #ident: ::core::marker::PhantomData });
mut_field_assignments.push(quote! { #ident: ::core::marker::PhantomData });
}
let mut new_generic_params = info.generic_params().clone();
new_generic_params.insert(0, syn::parse_quote! { 'this });
new_generic_params.insert(0, syn::parse_quote! { 'outer_borrow });
quote! { <#new_generic_params> }
};
let new_generic_args = {
let mut args = info.generic_arguments();
args.insert(0, quote! { 'this });
args.insert(0, quote! { 'outer_borrow });
args
};
for (ty, ident) in info.generic_consumers() {
fields.push(quote! { #ident: ::core::marker::PhantomData<#ty> });
mut_fields.push(quote! { #ident: ::core::marker::PhantomData<#ty> });
field_assignments.push(quote! { #ident: ::core::marker::PhantomData });
mut_field_assignments.push(quote! { #ident: ::core::marker::PhantomData });
}
let new_generic_params = info.borrowed_generic_params();
let new_generic_args = info.borrowed_generic_arguments();

let struct_documentation = format!(
concat!(
Expand Down
20 changes: 20 additions & 0 deletions ouroboros_macro/src/info_structures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,30 @@ impl StructInfo {
&self.generics.params
}

/// Same as generic_params but with 'this and 'outer_borrow prepended.
pub fn borrowed_generic_params(&self) -> TokenStream {
if self.generic_params().is_empty() {
quote! { <'outer_borrow, 'this> }
} else {
let mut new_generic_params = self.generic_params().clone();
new_generic_params.insert(0, syn::parse_quote! { 'this });
new_generic_params.insert(0, syn::parse_quote! { 'outer_borrow });
quote! { <#new_generic_params> }
}
}

pub fn generic_arguments(&self) -> Vec<TokenStream> {
make_generic_arguments(&self.generics)
}

/// Same as generic_arguments but with 'outer_borrow and 'this prepended.
pub fn borrowed_generic_arguments(&self) -> Vec<TokenStream> {
let mut args = self.generic_arguments();
args.insert(0, quote! { 'this });
args.insert(0, quote! { 'outer_borrow });
args
}

pub fn generic_consumers(&self) -> impl Iterator<Item = (TokenStream, Ident)> {
make_generic_consumers(&self.generics)
}
Expand Down
15 changes: 14 additions & 1 deletion ouroboros_macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,17 @@ mod info_structures;
mod parse;
mod utils;

use crate::{generate::{constructor::create_builder_and_constructor, derives::create_derives, into_heads::make_into_heads, struc::create_actual_struct_def, 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 crate::{
generate::{
constructor::create_builder_and_constructor, derives::create_derives,
into_heads::make_into_heads, struc::create_actual_struct_def,
summon_borrowchk::generate_borrowchk_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 inflector::Inflector;
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
Expand All @@ -27,6 +37,8 @@ fn self_referencing_impl(

let actual_struct_def = create_actual_struct_def(&info)?;

let borrowchk_summoner = generate_borrowchk_summoner(&info)?;

let (builder_struct_name, builder_def, constructor_def) =
create_builder_and_constructor(&info, options, false)?;
let (async_builder_struct_name, async_builder_def, async_constructor_def) =
Expand Down Expand Up @@ -60,6 +72,7 @@ fn self_referencing_impl(
mod #mod_name {
use super::*;
#actual_struct_def
#borrowchk_summoner
#builder_def
#async_builder_def
#try_builder_def
Expand Down

0 comments on commit 9c4db7b

Please sign in to comment.