Skip to content

Commit 46c752a

Browse files
committed
Allow lifetime elision in Pin<&(mut) Self>
1 parent 572892c commit 46c752a

7 files changed

+165
-1
lines changed

src/librustc/middle/resolve_lifetime.rs

+28-1
Original file line numberDiff line numberDiff line change
@@ -2149,7 +2149,34 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
21492149
false
21502150
};
21512151

2152-
if let hir::TyKind::Rptr(lifetime_ref, ref mt) = inputs[0].node {
2152+
let mut self_arg = &inputs[0].node;
2153+
2154+
// Apply `self: &(mut) Self` elision rules even if nested in `Pin`.
2155+
loop {
2156+
if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) = *self_arg {
2157+
if let Res::Def(DefKind::Struct, def_id) = path.res {
2158+
if self.tcx.lang_items().pin_type() == Some(def_id) {
2159+
if let Some(args) = path
2160+
.segments
2161+
.last()
2162+
.and_then(|segment| segment.args.as_ref())
2163+
{
2164+
if args.args.len() == 1 {
2165+
if let GenericArg::Type(ty) = &args.args[0] {
2166+
self_arg = &ty.node;
2167+
// Keep dereferencing `self_arg` until we get to non-`Pin`
2168+
// types.
2169+
continue;
2170+
}
2171+
}
2172+
}
2173+
}
2174+
}
2175+
}
2176+
break;
2177+
}
2178+
2179+
if let hir::TyKind::Rptr(lifetime_ref, ref mt) = *self_arg {
21532180
if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) = mt.ty.node {
21542181
if is_self_ty(path.res) {
21552182
if let Some(&lifetime) = self.map.defs.get(&lifetime_ref.hir_id) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// compile-pass
2+
3+
use std::pin::Pin;
4+
use std::task::{Context, Poll};
5+
6+
struct Foo;
7+
8+
impl Foo {
9+
fn pin_ref(self: Pin<&Self>) -> Pin<&Self> { self }
10+
11+
fn pin_mut(self: Pin<&mut Self>) -> Pin<&mut Self> { self }
12+
13+
fn pin_pin_pin_ref(self: Pin<Pin<Pin<&Self>>>) -> Pin<Pin<Pin<&Self>>> { self }
14+
15+
fn pin_ref_impl_trait(self: Pin<&Self>) -> impl Clone + '_ { self }
16+
17+
fn b(self: Pin<&Foo>, f: &Foo) -> Pin<&Foo> { self }
18+
}
19+
20+
type Alias<T> = Pin<T>;
21+
impl Foo {
22+
fn bar<'a>(self: Alias<&Self>, arg: &'a ()) -> &() { arg }
23+
}
24+
25+
struct Bar<T: Unpin, U: Unpin> {
26+
field1: T,
27+
field2: U,
28+
}
29+
30+
impl<T: Unpin, U: Unpin> Bar<T, U> {
31+
fn fields(self: Pin<&mut Self>) -> (Pin<&mut T>, Pin<&mut U>) {
32+
let this = self.get_mut();
33+
(Pin::new(&mut this.field1), Pin::new(&mut this.field2))
34+
}
35+
}
36+
37+
trait AsyncBufRead {
38+
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>)
39+
-> Poll<std::io::Result<&[u8]>>;
40+
}
41+
42+
struct Baz(Vec<u8>);
43+
44+
impl AsyncBufRead for Baz {
45+
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>)
46+
-> Poll<std::io::Result<&[u8]>>
47+
{
48+
Poll::Ready(Ok(&self.get_mut().0))
49+
}
50+
}
51+
52+
fn main() {
53+
let mut foo = Foo;
54+
{ Pin::new(&foo).pin_ref() };
55+
{ Pin::new(&mut foo).pin_mut() };
56+
{ Pin::new(Pin::new(Pin::new(&foo))).pin_pin_pin_ref() };
57+
{ Pin::new(&foo).pin_ref_impl_trait() };
58+
let mut bar = Bar { field1: 0u8, field2: 1u8 };
59+
{ Pin::new(&mut bar).fields() };
60+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// compile-fail
2+
3+
use std::pin::Pin;
4+
5+
struct Foo;
6+
7+
impl Foo {
8+
fn f(self: Pin<&Self>) -> impl Clone { self } //~ ERROR cannot infer an appropriate lifetime
9+
}
10+
11+
fn main() {
12+
{ Pin::new(&Foo).f() };
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error: cannot infer an appropriate lifetime
2+
--> $DIR/arbitrary_self_types_pin_lifetime_impl_trait.rs:8:44
3+
|
4+
LL | fn f(self: Pin<&Self>) -> impl Clone { self }
5+
| ---------- ^^^^ ...but this borrow...
6+
| |
7+
| this return type evaluates to the `'static` lifetime...
8+
|
9+
note: ...can't outlive the anonymous lifetime #1 defined on the method body at 8:5
10+
--> $DIR/arbitrary_self_types_pin_lifetime_impl_trait.rs:8:5
11+
|
12+
LL | fn f(self: Pin<&Self>) -> impl Clone { self }
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
14+
help: you can add a constraint to the return type to make it last less than `'static` and match the anonymous lifetime #1 defined on the method body at 8:5
15+
|
16+
LL | fn f(self: Pin<&Self>) -> impl Clone + '_ { self }
17+
| ^^^^^^^^^^^^^^^
18+
19+
error: aborting due to previous error
20+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// compile-fail
2+
3+
use std::pin::Pin;
4+
5+
struct Foo;
6+
7+
impl Foo {
8+
fn a(self: Pin<&Foo>, f: &Foo) -> &Foo { f } //~ ERROR E0623
9+
10+
fn c(self: Pin<&Self>, f: &Foo, g: &Foo) -> (Pin<&Foo>, &Foo) { (self, f) } //~ ERROR E0623
11+
}
12+
13+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error[E0623]: lifetime mismatch
2+
--> $DIR/arbitrary_self_types_pin_lifetime_mismatch.rs:8:46
3+
|
4+
LL | fn a(self: Pin<&Foo>, f: &Foo) -> &Foo { f }
5+
| ---- ---- ^ ...but data from `f` is returned here
6+
| |
7+
| this parameter and the return type are declared with different lifetimes...
8+
9+
error[E0623]: lifetime mismatch
10+
--> $DIR/arbitrary_self_types_pin_lifetime_mismatch.rs:10:76
11+
|
12+
LL | fn c(self: Pin<&Self>, f: &Foo, g: &Foo) -> (Pin<&Foo>, &Foo) { (self, f) }
13+
| ---- ----------------- ^ ...but data from `f` is returned here
14+
| |
15+
| this parameter and the return type are declared with different lifetimes...
16+
17+
error: aborting due to 2 previous errors
18+

src/test/ui/self/self_lifetime.rs

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// compile-pass
2+
3+
struct Foo<'a>(&'a ());
4+
impl<'a> Foo<'a> {
5+
fn foo<'b>(self: &'b Foo<'a>) -> &() { self.0 }
6+
}
7+
8+
type Alias = Foo<'static>;
9+
impl Alias {
10+
fn bar<'a>(self: &Alias, arg: &'a ()) -> &() { arg }
11+
}
12+
13+
fn main() {}

0 commit comments

Comments
 (0)