Skip to content

Commit 6789630

Browse files
committed
internal: pin_data: allow lifetime to be shortened per field drop order
Add the outlive relations per field drop order. This allows a single lifetime to be used when a field potentially borrow from two different fields, by allowing the longer-living field lifetime to be shortened to a shorter-living field lifetime. Signed-off-by: Gary Guo <gary@garyguo.net>
1 parent 9af7f1b commit 6789630

3 files changed

Lines changed: 105 additions & 46 deletions

File tree

internal/src/pin_data.rs

Lines changed: 53 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use syn::{
1616

1717
use crate::{
1818
diagnostics::{DiagCtxt, ErrorGuaranteed},
19-
util::{Binder, LifetimeExt, TypeExt},
19+
util::{Binder, GenericsExt, LifetimeExt, TypeExt},
2020
};
2121

2222
pub(crate) mod kw {
@@ -293,14 +293,48 @@ pub(crate) fn pin_data(
293293
}
294294
}
295295

296+
// Create a lifetime parameter for each field.
297+
let field_lts: Vec<_> = fields
298+
.iter()
299+
.filter(|f| f.borrowed.is_some())
300+
.map(|f| Lifetime::from_ident(f.field.ident.as_ref().unwrap()))
301+
.collect();
302+
let field_lt_generics = Generics {
303+
lt_token: Some(Default::default()),
304+
params: field_lts
305+
.iter()
306+
.zip(std::iter::once(None).chain(field_lts.iter().map(Some)))
307+
.map(|(lt, prev)| {
308+
GenericParam::Lifetime(LifetimeParam {
309+
attrs: Vec::new(),
310+
lifetime: lt.clone(),
311+
colon_token: prev.map(|_| Default::default()),
312+
bounds: prev.iter().map(|&lt| lt.clone()).collect(),
313+
})
314+
})
315+
.collect(),
316+
gt_token: Some(Default::default()),
317+
where_clause: None,
318+
};
319+
296320
let struct_def = generate_struct_def(&struct_, &fields);
297321
let unpin_impl = generate_unpin_impl(&struct_.ident, &struct_.generics, &fields);
298322
let drop_impl = generate_drop_impl(&struct_.ident, &struct_.generics, args);
299-
let drop_check = generate_drop_check(dcx, &struct_, &fields);
300-
let projections =
301-
generate_projections(&struct_.vis, &struct_.ident, &struct_.generics, &fields);
302-
let the_pin_data =
303-
generate_the_pin_data(&struct_.vis, &struct_.ident, &struct_.generics, &fields);
323+
let drop_check = generate_drop_check(dcx, &struct_, &fields, &field_lt_generics);
324+
let projections = generate_projections(
325+
&struct_.vis,
326+
&struct_.ident,
327+
&struct_.generics,
328+
&fields,
329+
&field_lt_generics,
330+
);
331+
let the_pin_data = generate_the_pin_data(
332+
&struct_.vis,
333+
&struct_.ident,
334+
&struct_.generics,
335+
&fields,
336+
&field_lt_generics,
337+
);
304338

305339
Ok(quote! {
306340
#struct_def
@@ -518,6 +552,7 @@ fn generate_drop_check(
518552
dcx: &mut DiagCtxt,
519553
struct_: &ItemStruct,
520554
fields: &[FieldInfo<'_>],
555+
field_lt_generics: &Generics,
521556
) -> TokenStream {
522557
let struct_name = &struct_.ident;
523558
// If the struct is not self-referential then we can just skip. However, still leave a
@@ -554,22 +589,12 @@ fn generate_drop_check(
554589
}
555590
}
556591

557-
// Create a lifetime parameter for each field.
558-
let field_lt_params: Vec<_> = fields
559-
.iter()
560-
.filter(|f| f.borrowed.is_some())
561-
.map(|f| {
562-
GenericParam::Lifetime(LifetimeParam::new(Lifetime::from_ident(
563-
f.field.ident.as_ref().unwrap(),
564-
)))
565-
})
566-
.collect();
567-
568592
let mut generics_with_field_lt = struct_.generics.clone();
569593
generics_with_field_lt
570594
.params
571-
.extend(field_lt_params.iter().cloned());
595+
.extend(field_lt_generics.params.iter().cloned());
572596

597+
let field_lt_for_generics = field_lt_generics.for_generics();
573598
let (_, ty_generics_with_field_lt, _) = generics_with_field_lt.split_for_impl();
574599
let (impl_generics, ty_generics, whr) = struct_.generics.split_for_impl();
575600

@@ -668,7 +693,7 @@ fn generate_drop_check(
668693
fn __drop_check #impl_generics (
669694
// This must be present so the function can observe the implied bounds.
670695
_: &#struct_name #ty_generics,
671-
f: impl for<#(#field_lt_params,)*>::core::ops::FnOnce(__DropCheck #ty_generics_with_field_lt)
696+
f: impl #field_lt_for_generics ::core::ops::FnOnce(__DropCheck #ty_generics_with_field_lt)
672697
) #whr {
673698
#(#guards)*
674699

@@ -684,6 +709,7 @@ fn generate_projections(
684709
ident: &Ident,
685710
generics: &Generics,
686711
fields: &[FieldInfo<'_>],
712+
field_lt_generics: &Generics,
687713
) -> TokenStream {
688714
let pin_lt = Lifetime::new("'__pin", Span::mixed_site());
689715

@@ -695,19 +721,11 @@ fn generate_projections(
695721
);
696722
let (_, ty_generics_with_pin_lt, whr) = generics_with_pin_lt.split_for_impl();
697723

698-
let field_lt_params: Vec<_> = fields
699-
.iter()
700-
.filter(|f| f.borrowed.is_some())
701-
.map(|f| {
702-
GenericParam::Lifetime(LifetimeParam::new(Lifetime::from_ident(
703-
f.field.ident.as_ref().unwrap(),
704-
)))
705-
})
706-
.collect();
707724
let mut generics_with_field_lt = generics.clone();
708725
generics_with_field_lt
709726
.params
710-
.extend(field_lt_params.iter().cloned());
727+
.extend(field_lt_generics.params.iter().cloned());
728+
let field_lt_for_generics = field_lt_generics.for_generics();
711729
let (_, ty_generics_with_field_lt, _) = generics_with_field_lt.split_for_impl();
712730

713731
let mut generics_with_pin_field_lt = generics_with_field_lt.clone();
@@ -1008,7 +1026,7 @@ fn generate_projections(
10081026
#[inline]
10091027
#vis fn with_project<'__pin, R>(
10101028
self: ::core::pin::Pin<&'__pin mut Self>,
1011-
f: impl for<#(#field_lt_params,)*>::core::ops::FnOnce(__ProjectionLt #ty_generics_with_pin_field_lt) -> R
1029+
f: impl #field_lt_for_generics ::core::ops::FnOnce(__ProjectionLt #ty_generics_with_pin_field_lt) -> R
10121030
) -> R {
10131031
// SAFETY: we only give access to `&mut` for fields not structurally pinned.
10141032
let #this = unsafe { ::core::pin::Pin::get_unchecked_mut(self) };
@@ -1028,22 +1046,14 @@ fn generate_the_pin_data(
10281046
struct_name: &Ident,
10291047
generics: &Generics,
10301048
fields: &[FieldInfo<'_>],
1049+
field_lt_generics: &Generics,
10311050
) -> TokenStream {
1032-
let field_lt_params: Vec<_> = fields
1033-
.iter()
1034-
.filter(|f| f.borrowed.is_some())
1035-
.map(|f| {
1036-
GenericParam::Lifetime(LifetimeParam::new(Lifetime::from_ident(
1037-
f.field.ident.as_ref().unwrap(),
1038-
)))
1039-
})
1040-
.collect();
1041-
10421051
let mut generics_with_field_lt = generics.clone();
10431052
generics_with_field_lt
10441053
.params
1045-
.extend(field_lt_params.iter().cloned());
1054+
.extend(field_lt_generics.params.iter().cloned());
10461055

1056+
let field_lt_for_generics = field_lt_generics.for_generics();
10471057
let (impl_generics_with_lt, ty_generics_with_field_lt, _) =
10481058
generics_with_field_lt.split_for_impl();
10491059
let (impl_generics, ty_generics, whr) = generics.split_for_impl();
@@ -1189,14 +1199,14 @@ fn generate_the_pin_data(
11891199
#[inline(always)]
11901200
#vis fn __make_closure<__F, __E>(self, f: __F) -> __F
11911201
where
1192-
__F: for<#(#field_lt_params,)*>::core::ops::FnOnce(*mut #struct_name #ty_generics, __PinDataLt #ty_generics_with_field_lt) ->
1202+
__F: #field_lt_for_generics ::core::ops::FnOnce(*mut #struct_name #ty_generics, __PinDataLt #ty_generics_with_field_lt) ->
11931203
::core::result::Result<::pin_init::__internal::InitOk, __E>,
11941204
{
11951205
f
11961206
}
11971207

11981208
#[inline(always)]
1199-
fn __with_lt<#(#field_lt_params,)*>(self) -> __PinDataLt #ty_generics_with_field_lt {
1209+
fn __with_lt #field_lt_generics(self) -> __PinDataLt #ty_generics_with_field_lt {
12001210
__PinDataLt { __phantom: ::core::marker::PhantomData }
12011211
}
12021212
}

internal/src/util.rs

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22

33
use std::collections::BTreeSet;
44

5-
use proc_macro2::Span;
6-
use quote::quote;
5+
use proc_macro2::{Span, TokenStream};
6+
use quote::{quote, ToTokens};
77
use syn::punctuated::Punctuated;
88
use syn::visit::Visit;
99
use syn::visit_mut::VisitMut;
10-
use syn::{BoundLifetimes, GenericParam, Ident, LifetimeParam};
10+
use syn::{BoundLifetimes, GenericParam, Generics, Ident, LifetimeParam, Token};
1111
use syn::{Lifetime, Type};
1212

1313
pub(crate) trait TypeExt {
@@ -210,3 +210,26 @@ impl LifetimeExt for Lifetime {
210210
}
211211
}
212212
}
213+
214+
pub(crate) struct ForGenerics<'a>(&'a Generics);
215+
216+
pub(crate) trait GenericsExt {
217+
fn for_generics(&self) -> ForGenerics<'_>;
218+
}
219+
220+
impl GenericsExt for Generics {
221+
fn for_generics(&self) -> ForGenerics<'_> {
222+
ForGenerics(self)
223+
}
224+
}
225+
226+
impl ToTokens for ForGenerics<'_> {
227+
fn to_tokens(&self, tokens: &mut TokenStream) {
228+
if self.0.params.is_empty() {
229+
return;
230+
}
231+
232+
<Token![for]>::default().to_tokens(tokens);
233+
self.0.split_for_impl().1.to_tokens(tokens);
234+
}
235+
}

tests/selfref_shorten.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
use pin_init::*;
2+
3+
#[pin_data]
4+
struct SelfRef {
5+
#[borrows(foo, bar)]
6+
part: &'foo str,
7+
foo: String,
8+
bar: String,
9+
}
10+
11+
#[test]
12+
fn self_ref() {
13+
stack_pin_init!(let foo = pin_init!(SelfRef {
14+
foo: "hello world".to_owned(),
15+
bar: "hello world".to_owned(),
16+
part: &foo[..5],
17+
}));
18+
19+
stack_pin_init!(let bar = pin_init!(SelfRef {
20+
foo: "hello world".to_owned(),
21+
bar: "hello world".to_owned(),
22+
// In this case, we borrow from a field that lives longers.
23+
// We're allowed to coerce it into a shorter-living lifetime.
24+
part: &bar[..5],
25+
}));
26+
}

0 commit comments

Comments
 (0)