Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
b19afb0
commit 8ba9309
Showing
11 changed files
with
212 additions
and
78 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
use ouroboros::self_referencing; | ||
|
||
pub struct PrintStrRef<'a>(&'a str); | ||
|
||
impl Drop for PrintStrRef<'_> { | ||
fn drop(&mut self) { | ||
println!("Dropping {}", self.0); | ||
} | ||
} | ||
|
||
#[self_referencing] | ||
pub struct Tricky { | ||
data1: String, | ||
#[borrows(data1)] | ||
#[covariant] | ||
ref1: PrintStrRef<'this>, | ||
data2: String, | ||
} | ||
|
||
fn main() { | ||
let mut t = Tricky::new( | ||
"A".to_owned(), | ||
|a| PrintStrRef(a), | ||
"B".to_owned(), | ||
); | ||
t.with_mut(|fields| { | ||
*fields.ref1 = PrintStrRef(fields.data2); | ||
}); | ||
drop(t); | ||
} |
26 changes: 26 additions & 0 deletions
26
examples/src/fail_tests/swap_refs_for_use_after_free.stderr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
error[E0597]: `t` does not live long enough | ||
--> src/fail_tests/swap_refs_for_use_after_free.rs:26:5 | ||
| | ||
26 | / t.with_mut(|fields| { | ||
27 | | *fields.ref1 = PrintStrRef(fields.data2); | ||
28 | | }); | ||
| | ^ | ||
| | | | ||
| |______borrowed value does not live long enough | ||
| argument requires that `t` is borrowed for `'static` | ||
29 | drop(t); | ||
30 | } | ||
| - `t` dropped here while still borrowed | ||
|
||
error[E0505]: cannot move out of `t` because it is borrowed | ||
--> src/fail_tests/swap_refs_for_use_after_free.rs:29:10 | ||
| | ||
26 | / t.with_mut(|fields| { | ||
27 | | *fields.ref1 = PrintStrRef(fields.data2); | ||
28 | | }); | ||
| | - | ||
| | | | ||
| |______borrow of `t` occurs here | ||
| argument requires that `t` is borrowed for `'static` | ||
29 | drop(t); | ||
| ^ move out of `t` occurs here |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
use crate::{ | ||
info_structures::{FieldType, Options, StructInfo}, | ||
utils::{replace_this_with_lifetime, uses_this_lifetime}, | ||
}; | ||
use itertools::Itertools; | ||
use proc_macro2::{Span, TokenStream}; | ||
use quote::{format_ident, quote}; | ||
use syn::{Error, Lifetime, WhereClause}; | ||
|
||
pub fn make_with_all_mut_function( | ||
info: &StructInfo, | ||
options: Options, | ||
) -> Result<(TokenStream, TokenStream), Error> { | ||
let visibility = if options.do_pub_extras { | ||
info.vis.clone() | ||
} else { | ||
syn::parse_quote! { pub(super) } | ||
}; | ||
let mut mut_fields = Vec::new(); | ||
let mut mut_field_assignments = Vec::new(); | ||
let mut lifetime_idents = 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; | ||
let lifetime = format_ident!("this{}", lifetime_idents.len()); | ||
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()); | ||
if field.field_type == FieldType::Tail { | ||
mut_fields.push(quote! { #visibility #field_name: &'outer_borrow mut #field_type }); | ||
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( | ||
&*this.#field_name | ||
) | ||
} }; | ||
let lt = Lifetime::new(&format!("'{}", lifetime.to_string()), Span::call_site()); | ||
mut_fields.push(quote! { #visibility #field_name: &#lt #field_type }); | ||
mut_field_assignments.push(ass); | ||
} else if field.field_type == FieldType::BorrowedMut { | ||
// Add nothing because we cannot borrow something that has already been mutably | ||
// borrowed. | ||
} | ||
} | ||
|
||
for (ty, ident) in info.generic_consumers() { | ||
mut_fields.push(quote! { #ident: ::core::marker::PhantomData<#ty> }); | ||
mut_field_assignments.push(quote! { #ident: ::core::marker::PhantomData }); | ||
} | ||
|
||
let mut new_generic_params = info.generic_params().clone(); | ||
for lt in &lifetime_idents { | ||
let lt = Lifetime::new(&format!("'{}", lt.to_string()), Span::call_site()); | ||
new_generic_params.insert(0, syn::parse_quote! { #lt }); | ||
} | ||
new_generic_params.insert(0, syn::parse_quote! { 'outer_borrow }); | ||
let mut new_generic_args = info.generic_arguments(); | ||
let mut lifetimes = Vec::new(); | ||
for lt in &lifetime_idents { | ||
let lt = Lifetime::new(&format!("'{}", lt.to_string()), Span::call_site()); | ||
lifetimes.push(lt.clone()); | ||
new_generic_args.insert(0, quote! { #lt }); | ||
} | ||
new_generic_args.insert(0, quote! { 'outer_borrow }); | ||
|
||
let mut_struct_documentation = format!( | ||
concat!( | ||
"A struct for holding mutable references to all ", | ||
"[tail fields](https://docs.rs/ouroboros/latest/ouroboros/attr.self_referencing.html#definitions) in an instance of ", | ||
"[`{0}`]({0})." | ||
), | ||
info.ident.to_string() | ||
); | ||
let fake_lifetime = Lifetime::new(&format!("'{}", info.fake_lifetime()), Span::call_site()); | ||
let mut generic_where = if let Some(clause) = &info.generics.where_clause { | ||
clause.clone() | ||
} else { | ||
syn::parse_quote! { where } | ||
}; | ||
for lt in &lifetime_idents { | ||
let lt = Lifetime::new(&format!("'{}", lt.to_string()), Span::call_site()); | ||
let extra: WhereClause = syn::parse_quote! { where #fake_lifetime: #lt }; | ||
generic_where | ||
.predicates | ||
.extend(extra.predicates.into_iter()); | ||
} | ||
for (outlives, lt) in lifetime_idents.iter().tuple_windows() { | ||
let lt = Lifetime::new(&format!("'{}", lt.to_string()), Span::call_site()); | ||
let outlives = Lifetime::new(&format!("'{}", outlives.to_string()), Span::call_site()); | ||
let extra: WhereClause = syn::parse_quote! { where #lt: #outlives }; | ||
generic_where | ||
.predicates | ||
.extend(extra.predicates.into_iter()); | ||
} | ||
let struct_defs = quote! { | ||
#[doc=#mut_struct_documentation] | ||
#visibility struct BorrowedMutFields <#new_generic_params> #generic_where { #(#mut_fields),* } | ||
}; | ||
let borrowed_mut_fields_type = quote! { BorrowedMutFields<#(#new_generic_args),*> }; | ||
let mut_documentation = concat!( | ||
"This method provides mutable references to all ", | ||
"[tail fields](https://docs.rs/ouroboros/latest/ouroboros/attr.self_referencing.html#definitions).", | ||
); | ||
let mut_documentation = if !options.do_no_doc { | ||
quote! { | ||
#[doc=#mut_documentation] | ||
} | ||
} else { | ||
quote! { #[doc(hidden)] } | ||
}; | ||
let fn_defs = quote! { | ||
#mut_documentation | ||
#[inline(always)] | ||
#visibility fn with_mut <'outer_borrow, ReturnType>( | ||
&'outer_borrow mut self, | ||
user: impl for<#(#lifetimes),*> ::core::ops::FnOnce(#borrowed_mut_fields_type) -> ReturnType | ||
) -> ReturnType { | ||
let this = unsafe { self.actual_data.assume_init_mut() }; | ||
user(BorrowedMutFields { | ||
#(#mut_field_assignments),* | ||
}) | ||
} | ||
}; | ||
Ok((struct_defs, fn_defs)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.