-
-
Notifications
You must be signed in to change notification settings - Fork 33
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Preserve original lifetime when performing pin-projection #67
Conversation
Assuming that this is indeed sound, it might be useful for Rust to provide this kind of upgrading behavior on Making it would for arbitrary user types would require introducing a weaker form of impl<'a, T> Pin<&'a mut T> {
fn as_mut_better(&mut self) -> Pin<'a mut T> {
/// The impl from this crate
}
} |
fn proj_trait_generics(&self) -> ProjTraitGenerics<'_> { | ||
//let mut orig_generics_for_impl = orig.clone(); | ||
//crate::utils::proj_generics(&mut orig_generics_for_impl, self.lifetime.clone()); | ||
//let (impl_generics, modified_ty_generics, _) = orig_generics_for_impl.split_for_impl(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please remove the above 3 lines.
let (orig_generics, _, orig_where_clause) = self.generics.split_for_impl(); | ||
let mut trait_generics = self.generics.clone(); | ||
crate::utils::proj_generics(&mut trait_generics, lifetime.clone()); | ||
//trait_generics.params.push(GenericParam::Lifetime(LifetimeDef::new(lifetime.clone()))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
//trait_generics.params.push(GenericParam::Lifetime(LifetimeDef::new(lifetime.clone()))); |
crate::utils::proj_generics(&mut orig_generics_for_impl, lifetime.clone()); | ||
let (impl_generics, modified_ty_generics, _) = orig_generics_for_impl.split_for_impl(); | ||
|
||
let (_, ty_generics, where_clause) = item.generics.split_for_impl();*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please remove the above 5 lines.
#[allow(unsafe_code)] | ||
let raw_mut: &mut T = unsafe { Pin::get_unchecked_mut(as_mut) }; | ||
|
||
// NExt, we transmute the mutable reference, changing its lifetime to 'a. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// NExt, we transmute the mutable reference, changing its lifetime to 'a. | |
// Next, we transmute the mutable reference, changing its lifetime to 'a. |
/// Normally, you'll never need to interact with this type | ||
/// directly - it's used internally by pin-project to allow expressing | ||
/// the proper lifetime. | ||
pub trait ProjectThrough<'a> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe this trait should be sealed?
mod private_project_through {
use core::pin::Pin;
pub trait Sealed {}
impl<T> Sealed for Pin<&mut T> {}
}
pub trait ProjectThrough<'a>: private_project_through::Sealed {
// ...
}
impl #impl_generics #proj_trait #ty_generics for ::core::pin::Pin<&mut #orig_ident #ty_generics> #where_clause { | ||
fn project<#lifetime>(&#lifetime mut self) -> #proj_ident #proj_ty_generics #where_clause { | ||
impl #impl_generics #proj_trait #ty_generics for ::core::pin::Pin<&#lifetime mut #orig_ident #orig_ty_generics> #where_clause { | ||
fn project(&mut self) -> #proj_ident #proj_ty_generics #where_clause { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fn project(&mut self) -> #proj_ident #proj_ty_generics #where_clause { | |
fn project(&mut self) -> #proj_ident #proj_ty_generics { |
Preexisting, but as it is already declared in the impl block, it looks unnecessary.
|
||
quote! { | ||
trait #proj_trait #orig_generics { | ||
fn project<#lifetime>(&#lifetime mut self) -> #proj_ident #proj_ty_generics #orig_where_clause; | ||
fn project(&mut self) -> #proj_ident #proj_ty_generics #orig_where_clause; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fn project(&mut self) -> #proj_ident #proj_ty_generics #orig_where_clause; | |
fn project(&mut self) -> #proj_ident #proj_ty_generics; |
impl #impl_generics #proj_trait #ty_generics for ::core::pin::Pin<&mut #orig_ident #ty_generics> #where_clause { | ||
fn project<#lifetime>(&#lifetime mut self) -> #proj_ident #proj_ty_generics #where_clause { | ||
impl #impl_generics #proj_trait #ty_generics for ::core::pin::Pin<&#lifetime mut #orig_ident #orig_ty_generics> #where_clause { | ||
fn project(&mut self) -> #proj_ident #proj_ty_generics #where_clause { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fn project(&mut self) -> #proj_ident #proj_ty_generics #where_clause { | |
fn project(&mut self) -> #proj_ident #proj_ty_generics { |
/// A helper trait to allow projecting through a mutable reference, | ||
/// while preserving lifetimes. | ||
/// | ||
/// Normally, `Pin::as_mut` can be used to convert a '&mut Pin' to a owned 'Pin' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// Normally, `Pin::as_mut` can be used to convert a '&mut Pin' to a owned 'Pin' | |
/// Normally, `Pin::as_mut` can be used to convert a '&mut Pin' to an owned 'Pin' |
/// while preserving lifetimes. | ||
/// | ||
/// Normally, `Pin::as_mut` can be used to convert a '&mut Pin' to a owned 'Pin' | ||
/// However, since `Pin::as_mut'`needs to work with generic `DerefMut` impls, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// However, since `Pin::as_mut'`needs to work with generic `DerefMut` impls, | |
/// However, since `Pin::as_mut` needs to work with generic `DerefMut` impls, |
// We accomplish this in four steps: | ||
// | ||
// First, call Pin::as_mut(self). This | ||
// converts our '&'p mut Pin<&'a mut T>` to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// converts our '&'p mut Pin<&'a mut T>` to | |
// converts our `&'p mut Pin<&'a mut T>` to |
// described above. | ||
// | ||
// This is the core of this method - everything else is just to deconstruct | ||
// and reconstrut a Pin |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// and reconstrut a Pin | |
// and reconstruct a Pin. |
It turns out that this is actually unsound - the output lifetime isn't tied to the input lifetime, so it's possible to create aliasing mutable references by calling I'm investigating if this can be made sound. |
Unfortunately, I think it's impossible to do this soundly in the There are two conflicting requirements:
fn get_pin_mut<'a>(mut self: Pin<&'a mut Self>) -> Pin<&'a mut T> {
self.project().pinned
} where the returned type outlives the It is impossible for one lifetime that both outlives and does not outlive another lifetime. Instead, I think we need to add a new method: |
Unfortunately, I think it's impossible to do this soundly in the There are two conflicting requirements:
fn get_pin_mut<'a>(mut self: Pin<&'a mut Self>) -> Pin<&'a mut T> {
self.project().pinned
} where the returned type outlives the It is impossible for one lifetime to both outlive and not outlive another lifetime. Instead, I think we need to add a new method: |
Superseded by #69 |
I am very confused by this PR, this seems entirely unnecessary -- why don't you reborrow the pinned reference? See what I wrote at #47 (comment). |
As described in issue #65, the
project
method currently loses lifetime information.Specifically, given a
&'a mut Pin<&'pin mut T>
, we would likeproject
to return aProjectionType<'pin>
- that is, a type with a lifetime tied to the lifetime of the inside of the originalPin
.However,
project
currently returns aProjectionType<'a>
- that is, a type with a lifetime tied to the lifetime of the 'outside' of thePin
. This means that pin-projected types will not live as long as the inside of thePin
they were derived from - which prevents returning fields from methods.This PR lifts this limitation, by ensuring that the returning projection struct/enum has a lifetime of
'pin
- that is, the lifetime in thePin<&'pin mut T>
that the projection was performed on.Most of this PR is just boilerplate to make things work on arbitrary structs and enums. The important part is the additional
unsafe
code added. Ultimately, it relies on this code being valid:That is, we need to be able to 'upgrade' the lifetime of the mutable reference returned from the
<&mut T as DerefMut>::deref_mut
. I believe this is sound -DerefMut
is essentially a no-op for mutable references. We're only adjusting the compiler's view of the lifetime, not performing any raw pointer tricks that might make Stacked Borrows angry.That being said, I'm not 100% sure that this is correct.
@RalfJung: Would you be able to take a look at this?
Note that this only relies on the built-in
DerefMut
impl for&mut T
, not any user-provided&mut DerefMut
impls. I think that this kind of 'upgrading' logic could potentially be extended to user-provided types that fulfill the requirement ofStableDeref
- that is, the mutable references they return live for as long as the original type. However, pin-project doesn't need to care about this.