Skip to content

Commit ece18b7

Browse files
authored
Rollup merge of rust-lang#61207 - taiki-e:arbitrary_self_types-lifetime-elision-2, r=Centril
Allow lifetime elision in `Pin<&(mut) Self>` This PR changes `self: &(mut) S` elision rules to instead visit the type of `self` and look for `&(mut) S` (where `is_self_ty(S)`) within it Replaces rust-lang#60944 Closes rust-lang#52675 r? @eddyb cc @cramertj @Centril @withoutboats @scottmcm
2 parents c798dff + 05f67a2 commit ece18b7

37 files changed

+1560
-33
lines changed

src/librustc/middle/resolve_lifetime.rs

+62-33
Original file line numberDiff line numberDiff line change
@@ -2146,48 +2146,77 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
21462146
// First (determined here), if `self` is by-reference, then the
21472147
// implied output region is the region of the self parameter.
21482148
if has_self {
2149-
// Look for `self: &'a Self` - also desugared from `&'a self`,
2150-
// and if that matches, use it for elision and return early.
2151-
let is_self_ty = |res: Res| {
2152-
if let Res::SelfTy(..) = res {
2153-
return true;
2154-
}
2155-
2156-
// Can't always rely on literal (or implied) `Self` due
2157-
// to the way elision rules were originally specified.
2158-
let impl_self = impl_self.map(|ty| &ty.node);
2159-
if let Some(&hir::TyKind::Path(hir::QPath::Resolved(None, ref path))) = impl_self {
2160-
match path.res {
2161-
// Whitelist the types that unambiguously always
2162-
// result in the same type constructor being used
2163-
// (it can't differ between `Self` and `self`).
2164-
Res::Def(DefKind::Struct, _)
2165-
| Res::Def(DefKind::Union, _)
2166-
| Res::Def(DefKind::Enum, _)
2167-
| Res::PrimTy(_) => {
2168-
return res == path.res
2149+
struct SelfVisitor<'a> {
2150+
map: &'a NamedRegionMap,
2151+
impl_self: Option<&'a hir::TyKind>,
2152+
lifetime: Set1<Region>,
2153+
}
2154+
2155+
impl SelfVisitor<'_> {
2156+
// Look for `self: &'a Self` - also desugared from `&'a self`,
2157+
// and if that matches, use it for elision and return early.
2158+
fn is_self_ty(&self, res: Res) -> bool {
2159+
if let Res::SelfTy(..) = res {
2160+
return true;
2161+
}
2162+
2163+
// Can't always rely on literal (or implied) `Self` due
2164+
// to the way elision rules were originally specified.
2165+
if let Some(&hir::TyKind::Path(hir::QPath::Resolved(None, ref path))) =
2166+
self.impl_self
2167+
{
2168+
match path.res {
2169+
// Whitelist the types that unambiguously always
2170+
// result in the same type constructor being used
2171+
// (it can't differ between `Self` and `self`).
2172+
Res::Def(DefKind::Struct, _)
2173+
| Res::Def(DefKind::Union, _)
2174+
| Res::Def(DefKind::Enum, _)
2175+
| Res::PrimTy(_) => {
2176+
return res == path.res
2177+
}
2178+
_ => {}
21692179
}
2170-
_ => {}
21712180
}
2181+
2182+
false
21722183
}
2184+
}
21732185

2174-
false
2175-
};
2186+
impl<'a> Visitor<'a> for SelfVisitor<'a> {
2187+
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'a> {
2188+
NestedVisitorMap::None
2189+
}
21762190

2177-
if let hir::TyKind::Rptr(lifetime_ref, ref mt) = inputs[0].node {
2178-
if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) = mt.ty.node {
2179-
if is_self_ty(path.res) {
2180-
if let Some(&lifetime) = self.map.defs.get(&lifetime_ref.hir_id) {
2181-
let scope = Scope::Elision {
2182-
elide: Elide::Exact(lifetime),
2183-
s: self.scope,
2184-
};
2185-
self.with(scope, |_, this| this.visit_ty(output));
2186-
return;
2191+
fn visit_ty(&mut self, ty: &'a hir::Ty) {
2192+
if let hir::TyKind::Rptr(lifetime_ref, ref mt) = ty.node {
2193+
if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) = mt.ty.node
2194+
{
2195+
if self.is_self_ty(path.res) {
2196+
if let Some(lifetime) = self.map.defs.get(&lifetime_ref.hir_id) {
2197+
self.lifetime.insert(*lifetime);
2198+
}
2199+
}
21872200
}
21882201
}
2202+
intravisit::walk_ty(self, ty)
21892203
}
21902204
}
2205+
2206+
let mut visitor = SelfVisitor {
2207+
map: self.map,
2208+
impl_self: impl_self.map(|ty| &ty.node),
2209+
lifetime: Set1::Empty,
2210+
};
2211+
visitor.visit_ty(&inputs[0]);
2212+
if let Set1::One(lifetime) = visitor.lifetime {
2213+
let scope = Scope::Elision {
2214+
elide: Elide::Exact(lifetime),
2215+
s: self.scope,
2216+
};
2217+
self.with(scope, |_, this| this.visit_ty(output));
2218+
return;
2219+
}
21912220
}
21922221

21932222
// 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)