Skip to content

Commit b23bbd8

Browse files
committed
Rollup merge of #63254 - taiki-e:arbitrary_self_types-lifetime-elision-2-beta, r=Centril
2 parents 8effd53 + d27529f commit b23bbd8

37 files changed

+1560
-33
lines changed

src/librustc/middle/resolve_lifetime.rs

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

2145-
false
2146-
};
2157+
impl<'a> Visitor<'a> for SelfVisitor<'a> {
2158+
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'a> {
2159+
NestedVisitorMap::None
2160+
}
21472161

2148-
if let hir::TyKind::Rptr(lifetime_ref, ref mt) = inputs[0].node {
2149-
if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) = mt.ty.node {
2150-
if is_self_ty(path.res) {
2151-
if let Some(&lifetime) = self.map.defs.get(&lifetime_ref.hir_id) {
2152-
let scope = Scope::Elision {
2153-
elide: Elide::Exact(lifetime),
2154-
s: self.scope,
2155-
};
2156-
self.with(scope, |_, this| this.visit_ty(output));
2157-
return;
2162+
fn visit_ty(&mut self, ty: &'a hir::Ty) {
2163+
if let hir::TyKind::Rptr(lifetime_ref, ref mt) = ty.node {
2164+
if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) = mt.ty.node
2165+
{
2166+
if self.is_self_ty(path.res) {
2167+
if let Some(lifetime) = self.map.defs.get(&lifetime_ref.hir_id) {
2168+
self.lifetime.insert(*lifetime);
2169+
}
2170+
}
21582171
}
21592172
}
2173+
intravisit::walk_ty(self, ty)
21602174
}
21612175
}
2176+
2177+
let mut visitor = SelfVisitor {
2178+
map: self.map,
2179+
impl_self: impl_self.map(|ty| &ty.node),
2180+
lifetime: Set1::Empty,
2181+
};
2182+
visitor.visit_ty(&inputs[0]);
2183+
if let Set1::One(lifetime) = visitor.lifetime {
2184+
let scope = Scope::Elision {
2185+
elide: Elide::Exact(lifetime),
2186+
s: self.scope,
2187+
};
2188+
self.with(scope, |_, this| this.visit_ty(output));
2189+
return;
2190+
}
21622191
}
21632192

21642193
// 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+
// check-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/elision/README.md

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
Test cases intended to document behavior and try to exhaustively
2+
explore the combinations.
3+
4+
## Confidence
5+
6+
These tests are not yet considered 100% normative, in that some
7+
aspects of the current behavior are not desirable. This is expressed
8+
in the "confidence" field in the following table. Values:
9+
10+
| Confidence | Interpretation |
11+
| --- | --- |
12+
| 100% | this will remain recommended behavior |
13+
| 75% | unclear whether we will continue to accept this |
14+
| 50% | this will likely be deprecated but remain valid |
15+
| 25% | this could change in the future |
16+
| 0% | this is definitely bogus and will likely change in the future in *some* way |
17+
18+
## Tests
19+
20+
| Test file | `Self` type | Pattern | Current elision behavior | Confidence |
21+
| --- | --- | --- | --- | --- |
22+
| `self.rs` | `Struct` | `Self` | ignore `self` parameter | 100% |
23+
| `struct.rs` | `Struct` | `Struct` | ignore `self` parameter | 100% |
24+
| `alias.rs` | `Struct` | `Alias` | ignore `self` parameter | 100% |
25+
| `ref-self.rs` | `Struct` | `&Self` | take lifetime from `&Self` | 100% |
26+
| `ref-mut-self.rs` | `Struct` | `&mut Self` | take lifetime from `&mut Self` | 100% |
27+
| `ref-struct.rs` | `Struct` | `&Struct` | take lifetime from `&Self` | 50% |
28+
| `ref-mut-struct.rs` | `Struct` | `&mut Struct` | take lifetime from `&mut Self` | 50% |
29+
| `ref-alias.rs` | `Struct` | `&Alias` | ignore `Alias` | 0% |
30+
| `ref-mut-alias.rs` | `Struct` | `&mut Alias` | ignore `Alias` | 0% |
31+
| `lt-self.rs` | `Struct<'a>` | `Self` | ignore `Self` (and hence `'a`) | 25% |
32+
| `lt-struct.rs` | `Struct<'a>` | `Self` | ignore `Self` (and hence `'a`) | 0% |
33+
| `lt-alias.rs` | `Alias<'a>` | `Self` | ignore `Self` (and hence `'a`) | 0% |
34+
| `lt-ref-self.rs` | `Struct<'a>` | `&Self` | take lifetime from `&Self` | 75% |
35+
36+
In each case, we test the following patterns:
37+
38+
- `self: XXX`
39+
- `self: Box<XXX>`
40+
- `self: Pin<XXX>`
41+
- `self: Box<Box<XXX>>`
42+
- `self: Box<Pin<XXX>>`
43+
44+
In the non-reference cases, `Pin` causes errors so we substitute `Rc`.

src/test/ui/self/elision/alias.rs

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// check-pass
2+
3+
#![feature(arbitrary_self_types)]
4+
#![allow(non_snake_case)]
5+
6+
use std::rc::Rc;
7+
8+
struct Struct { }
9+
10+
type Alias = Struct;
11+
12+
impl Struct {
13+
// Test using an alias for `Struct`:
14+
15+
fn alias(self: Alias, f: &u32) -> &u32 {
16+
f
17+
}
18+
19+
fn box_Alias(self: Box<Alias>, f: &u32) -> &u32 {
20+
f
21+
}
22+
23+
fn rc_Alias(self: Rc<Alias>, f: &u32) -> &u32 {
24+
f
25+
}
26+
27+
fn box_box_Alias(self: Box<Box<Alias>>, f: &u32) -> &u32 {
28+
f
29+
}
30+
31+
fn box_rc_Alias(self: Box<Rc<Alias>>, f: &u32) -> &u32 {
32+
f
33+
}
34+
}
35+
36+
fn main() { }

0 commit comments

Comments
 (0)