Skip to content

Commit 9d98f8f

Browse files
committed
Auto merge of #62209 - nikomatsakis:arbitrary_self_types-lifetime-elision-2-beta, r=Centril
Emergency backport of "arbitrary self types lifetime elision 2" this is a backport of #61207 per the discussion in [compiler steering meeting](https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/steering.20meeting.202019-06-28) r? @Centril
2 parents 09a353c + 83dc1b5 commit 9d98f8f

9 files changed

+254
-33
lines changed

src/librustc/middle/resolve_lifetime.rs

+62-33
Original file line numberDiff line numberDiff line change
@@ -2126,48 +2126,77 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
21262126
// First (determined here), if `self` is by-reference, then the
21272127
// implied output region is the region of the self parameter.
21282128
if has_self {
2129-
// Look for `self: &'a Self` - also desugared from `&'a self`,
2130-
// and if that matches, use it for elision and return early.
2131-
let is_self_ty = |res: Res| {
2132-
if let Res::SelfTy(..) = res {
2133-
return true;
2134-
}
2135-
2136-
// Can't always rely on literal (or implied) `Self` due
2137-
// to the way elision rules were originally specified.
2138-
let impl_self = impl_self.map(|ty| &ty.node);
2139-
if let Some(&hir::TyKind::Path(hir::QPath::Resolved(None, ref path))) = impl_self {
2140-
match path.res {
2141-
// Whitelist the types that unambiguously always
2142-
// result in the same type constructor being used
2143-
// (it can't differ between `Self` and `self`).
2144-
Res::Def(DefKind::Struct, _)
2145-
| Res::Def(DefKind::Union, _)
2146-
| Res::Def(DefKind::Enum, _)
2147-
| Res::PrimTy(_) => {
2148-
return res == path.res
2129+
struct SelfVisitor<'a> {
2130+
map: &'a NamedRegionMap,
2131+
impl_self: Option<&'a hir::TyKind>,
2132+
lifetime: Set1<Region>,
2133+
}
2134+
2135+
impl SelfVisitor<'_> {
2136+
// Look for `self: &'a Self` - also desugared from `&'a self`,
2137+
// and if that matches, use it for elision and return early.
2138+
fn is_self_ty(&self, res: Res) -> bool {
2139+
if let Res::SelfTy(..) = res {
2140+
return true;
2141+
}
2142+
2143+
// Can't always rely on literal (or implied) `Self` due
2144+
// to the way elision rules were originally specified.
2145+
if let Some(&hir::TyKind::Path(hir::QPath::Resolved(None, ref path))) =
2146+
self.impl_self
2147+
{
2148+
match path.res {
2149+
// Whitelist the types that unambiguously always
2150+
// result in the same type constructor being used
2151+
// (it can't differ between `Self` and `self`).
2152+
Res::Def(DefKind::Struct, _)
2153+
| Res::Def(DefKind::Union, _)
2154+
| Res::Def(DefKind::Enum, _)
2155+
| Res::PrimTy(_) => {
2156+
return res == path.res
2157+
}
2158+
_ => {}
21492159
}
2150-
_ => {}
21512160
}
2161+
2162+
false
21522163
}
2164+
}
21532165

2154-
false
2155-
};
2166+
impl<'a> Visitor<'a> for SelfVisitor<'a> {
2167+
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'a> {
2168+
NestedVisitorMap::None
2169+
}
21562170

2157-
if let hir::TyKind::Rptr(lifetime_ref, ref mt) = inputs[0].node {
2158-
if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) = mt.ty.node {
2159-
if is_self_ty(path.res) {
2160-
if let Some(&lifetime) = self.map.defs.get(&lifetime_ref.hir_id) {
2161-
let scope = Scope::Elision {
2162-
elide: Elide::Exact(lifetime),
2163-
s: self.scope,
2164-
};
2165-
self.with(scope, |_, this| this.visit_ty(output));
2166-
return;
2171+
fn visit_ty(&mut self, ty: &'a hir::Ty) {
2172+
if let hir::TyKind::Rptr(lifetime_ref, ref mt) = ty.node {
2173+
if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) = mt.ty.node
2174+
{
2175+
if self.is_self_ty(path.res) {
2176+
if let Some(lifetime) = self.map.defs.get(&lifetime_ref.hir_id) {
2177+
self.lifetime.insert(*lifetime);
2178+
}
2179+
}
21672180
}
21682181
}
2182+
intravisit::walk_ty(self, ty)
21692183
}
21702184
}
2185+
2186+
let mut visitor = SelfVisitor {
2187+
map: self.map,
2188+
impl_self: impl_self.map(|ty| &ty.node),
2189+
lifetime: Set1::Empty,
2190+
};
2191+
visitor.visit_ty(&inputs[0]);
2192+
if let Set1::One(lifetime) = visitor.lifetime {
2193+
let scope = Scope::Elision {
2194+
elide: Elide::Exact(lifetime),
2195+
s: self.scope,
2196+
};
2197+
self.with(scope, |_, this| this.visit_ty(output));
2198+
return;
2199+
}
21712200
}
21722201

21732202
// Second, if there was exactly one lifetime (either a substitution or a
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 ()) -> Alias<&Self> { self }
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,14 @@
1+
error: lifetime may not live long enough
2+
--> $DIR/arbitrary_self_types_pin_lifetime_impl_trait.rs:8:31
3+
|
4+
LL | fn f(self: Pin<&Self>) -> impl Clone { self }
5+
| - ^^^^^^^^^^ opaque type requires that `'1` must outlive `'static`
6+
| |
7+
| let's call the lifetime of this reference `'1`
8+
help: to allow this impl Trait to capture borrowed data with lifetime `'1`, add `'_` as a constraint
9+
|
10+
LL | fn f(self: Pin<&Self>) -> impl Clone + '_ { self }
11+
| ^^^^^^^^^^^^^^^
12+
13+
error: aborting due to previous error
14+
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,28 @@
1+
error: lifetime may not live long enough
2+
--> $DIR/arbitrary_self_types_pin_lifetime_mismatch.rs:8:46
3+
|
4+
LL | fn a(self: Pin<&Foo>, f: &Foo) -> &Foo { f }
5+
| - - ^ function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
6+
| | |
7+
| | let's call the lifetime of this reference `'1`
8+
| let's call the lifetime of this reference `'2`
9+
10+
error: lifetime may not live long enough
11+
--> $DIR/arbitrary_self_types_pin_lifetime_mismatch.rs:10:69
12+
|
13+
LL | fn c(self: Pin<&Self>, f: &Foo, g: &Foo) -> (Pin<&Foo>, &Foo) { (self, f) }
14+
| - - ^^^^^^^^^ function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
15+
| | |
16+
| | let's call the lifetime of this reference `'1`
17+
| let's call the lifetime of this reference `'2`
18+
19+
error: lifetime may not live long enough
20+
--> $DIR/arbitrary_self_types_pin_lifetime_mismatch.rs:15:58
21+
|
22+
LL | fn bar<'a>(self: Alias<&Self>, arg: &'a ()) -> &() { arg }
23+
| -- ---- has type `std::pin::Pin<&'1 Foo>` ^^^ function was supposed to return data with lifetime `'1` but it is returning data with lifetime `'a`
24+
| |
25+
| lifetime `'a` defined here
26+
27+
error: aborting due to 3 previous errors
28+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
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+
type Alias<T> = Pin<T>;
14+
impl Foo {
15+
fn bar<'a>(self: Alias<&Self>, arg: &'a ()) -> &() { arg } //~ ERROR E0623
16+
}
17+
18+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
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[E0623]: lifetime mismatch
18+
--> $DIR/arbitrary_self_types_pin_lifetime_mismatch.rs:15:58
19+
|
20+
LL | fn bar<'a>(self: Alias<&Self>, arg: &'a ()) -> &() { arg }
21+
| ------ --- ^^^ ...but data from `arg` is returned here
22+
| |
23+
| this parameter and the return type are declared with different lifetimes...
24+
25+
error: aborting due to 3 previous errors
26+

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)