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
diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs
index 2402d0e..50715a4 100644
--- a/src/librustc/middle/resolve_lifetime.rs
+++ b/src/librustc/middle/resolve_lifetime.rs
@@ -2126,47 +2126,76 @@
         // First (determined here), if `self` is by-reference, then the
         // implied output region is the region of the self parameter.
         if has_self {
-            // Look for `self: &'a Self` - also desugared from `&'a self`,
-            // and if that matches, use it for elision and return early.
-            let is_self_ty = |res: Res| {
-                if let Res::SelfTy(..) = res {
-                    return true;
-                }
+            struct SelfVisitor<'a> {
+                map: &'a NamedRegionMap,
+                impl_self: Option<&'a hir::TyKind>,
+                lifetime: Set1<Region>,
+            }
 
-                // Can't always rely on literal (or implied) `Self` due
-                // to the way elision rules were originally specified.
-                let impl_self = impl_self.map(|ty| &ty.node);
-                if let Some(&hir::TyKind::Path(hir::QPath::Resolved(None, ref path))) = impl_self {
-                    match path.res {
-                        // Whitelist the types that unambiguously always
-                        // result in the same type constructor being used
-                        // (it can't differ between `Self` and `self`).
-                        Res::Def(DefKind::Struct, _)
-                        | Res::Def(DefKind::Union, _)
-                        | Res::Def(DefKind::Enum, _)
-                        | Res::PrimTy(_) => {
-                            return res == path.res
-                        }
-                        _ => {}
+            impl SelfVisitor<'_> {
+                // Look for `self: &'a Self` - also desugared from `&'a self`,
+                // and if that matches, use it for elision and return early.
+                fn is_self_ty(&self, res: Res) -> bool {
+                    if let Res::SelfTy(..) = res {
+                        return true;
                     }
+
+                    // Can't always rely on literal (or implied) `Self` due
+                    // to the way elision rules were originally specified.
+                    if let Some(&hir::TyKind::Path(hir::QPath::Resolved(None, ref path))) =
+                        self.impl_self
+                    {
+                        match path.res {
+                            // Whitelist the types that unambiguously always
+                            // result in the same type constructor being used
+                            // (it can't differ between `Self` and `self`).
+                            Res::Def(DefKind::Struct, _)
+                            | Res::Def(DefKind::Union, _)
+                            | Res::Def(DefKind::Enum, _)
+                            | Res::PrimTy(_) => {
+                                return res == path.res
+                            }
+                            _ => {}
+                        }
+                    }
+
+                    false
+                }
+            }
+
+            impl<'a> Visitor<'a> for SelfVisitor<'a> {
+                fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'a> {
+                    NestedVisitorMap::None
                 }
 
-                false
+                fn visit_ty(&mut self, ty: &'a hir::Ty) {
+                    if let hir::TyKind::Rptr(lifetime_ref, ref mt) = ty.node {
+                        if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) = mt.ty.node
+                        {
+                            if self.is_self_ty(path.res) {
+                                if let Some(lifetime) = self.map.defs.get(&lifetime_ref.hir_id) {
+                                    self.lifetime.insert(*lifetime);
+                                }
+                            }
+                        }
+                    }
+                    intravisit::walk_ty(self, ty)
+                }
+            }
+
+            let mut visitor = SelfVisitor {
+                map: self.map,
+                impl_self: impl_self.map(|ty| &ty.node),
+                lifetime: Set1::Empty,
             };
-
-            if let hir::TyKind::Rptr(lifetime_ref, ref mt) = inputs[0].node {
-                if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) = mt.ty.node {
-                    if is_self_ty(path.res) {
-                        if let Some(&lifetime) = self.map.defs.get(&lifetime_ref.hir_id) {
-                            let scope = Scope::Elision {
-                                elide: Elide::Exact(lifetime),
-                                s: self.scope,
-                            };
-                            self.with(scope, |_, this| this.visit_ty(output));
-                            return;
-                        }
-                    }
-                }
+            visitor.visit_ty(&inputs[0]);
+            if let Set1::One(lifetime) = visitor.lifetime {
+                let scope = Scope::Elision {
+                    elide: Elide::Exact(lifetime),
+                    s: self.scope,
+                };
+                self.with(scope, |_, this| this.visit_ty(output));
+                return;
             }
         }
 
diff --git a/src/test/ui/self/arbitrary_self_types_pin_lifetime.rs b/src/test/ui/self/arbitrary_self_types_pin_lifetime.rs
new file mode 100644
index 0000000..ba574ee
--- /dev/null
+++ b/src/test/ui/self/arbitrary_self_types_pin_lifetime.rs
@@ -0,0 +1,60 @@
+// compile-pass
+
+use std::pin::Pin;
+use std::task::{Context, Poll};
+
+struct Foo;
+
+impl Foo {
+    fn pin_ref(self: Pin<&Self>) -> Pin<&Self> { self }
+
+    fn pin_mut(self: Pin<&mut Self>) -> Pin<&mut Self> { self }
+
+    fn pin_pin_pin_ref(self: Pin<Pin<Pin<&Self>>>) -> Pin<Pin<Pin<&Self>>> { self }
+
+    fn pin_ref_impl_trait(self: Pin<&Self>) -> impl Clone + '_ { self }
+
+    fn b(self: Pin<&Foo>, f: &Foo) -> Pin<&Foo> { self }
+}
+
+type Alias<T> = Pin<T>;
+impl Foo {
+    fn bar<'a>(self: Alias<&Self>, arg: &'a ()) -> Alias<&Self> { self }
+}
+
+struct Bar<T: Unpin, U: Unpin> {
+    field1: T,
+    field2: U,
+}
+
+impl<T: Unpin, U: Unpin> Bar<T, U> {
+    fn fields(self: Pin<&mut Self>) -> (Pin<&mut T>, Pin<&mut U>) {
+        let this = self.get_mut();
+        (Pin::new(&mut this.field1), Pin::new(&mut this.field2))
+    }
+}
+
+trait AsyncBufRead {
+    fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>)
+        -> Poll<std::io::Result<&[u8]>>;
+}
+
+struct Baz(Vec<u8>);
+
+impl AsyncBufRead for Baz {
+    fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>)
+        -> Poll<std::io::Result<&[u8]>>
+    {
+        Poll::Ready(Ok(&self.get_mut().0))
+    }
+}
+
+fn main() {
+    let mut foo = Foo;
+    { Pin::new(&foo).pin_ref() };
+    { Pin::new(&mut foo).pin_mut() };
+    { Pin::new(Pin::new(Pin::new(&foo))).pin_pin_pin_ref() };
+    { Pin::new(&foo).pin_ref_impl_trait() };
+    let mut bar = Bar { field1: 0u8, field2: 1u8 };
+    { Pin::new(&mut bar).fields() };
+}
diff --git a/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.nll.stderr b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.nll.stderr
new file mode 100644
index 0000000..4967007
--- /dev/null
+++ b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.nll.stderr
@@ -0,0 +1,14 @@
+error: lifetime may not live long enough
+  --> $DIR/arbitrary_self_types_pin_lifetime_impl_trait.rs:8:31
+   |
+LL |     fn f(self: Pin<&Self>) -> impl Clone { self }
+   |                    -          ^^^^^^^^^^ opaque type requires that `'1` must outlive `'static`
+   |                    |
+   |                    let's call the lifetime of this reference `'1`
+help: to allow this impl Trait to capture borrowed data with lifetime `'1`, add `'_` as a constraint
+   |
+LL |     fn f(self: Pin<&Self>) -> impl Clone + '_ { self }
+   |                               ^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.rs b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.rs
new file mode 100644
index 0000000..ad89597
--- /dev/null
+++ b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.rs
@@ -0,0 +1,13 @@
+// compile-fail
+
+use std::pin::Pin;
+
+struct Foo;
+
+impl Foo {
+    fn f(self: Pin<&Self>) -> impl Clone { self } //~ ERROR cannot infer an appropriate lifetime
+}
+
+fn main() {
+    { Pin::new(&Foo).f() };
+}
diff --git a/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.stderr b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.stderr
new file mode 100644
index 0000000..5118280
--- /dev/null
+++ b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.stderr
@@ -0,0 +1,20 @@
+error: cannot infer an appropriate lifetime
+  --> $DIR/arbitrary_self_types_pin_lifetime_impl_trait.rs:8:44
+   |
+LL |     fn f(self: Pin<&Self>) -> impl Clone { self }
+   |                               ----------   ^^^^ ...but this borrow...
+   |                               |
+   |                               this return type evaluates to the `'static` lifetime...
+   |
+note: ...can't outlive the anonymous lifetime #1 defined on the method body at 8:5
+  --> $DIR/arbitrary_self_types_pin_lifetime_impl_trait.rs:8:5
+   |
+LL |     fn f(self: Pin<&Self>) -> impl Clone { self }
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+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
+   |
+LL |     fn f(self: Pin<&Self>) -> impl Clone + '_ { self }
+   |                               ^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.nll.stderr b/src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.nll.stderr
new file mode 100644
index 0000000..8a0f1a8
--- /dev/null
+++ b/src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.nll.stderr
@@ -0,0 +1,28 @@
+error: lifetime may not live long enough
+  --> $DIR/arbitrary_self_types_pin_lifetime_mismatch.rs:8:46
+   |
+LL |     fn a(self: Pin<&Foo>, f: &Foo) -> &Foo { f }
+   |                    -         -               ^ function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+   |                    |         |
+   |                    |         let's call the lifetime of this reference `'1`
+   |                    let's call the lifetime of this reference `'2`
+
+error: lifetime may not live long enough
+  --> $DIR/arbitrary_self_types_pin_lifetime_mismatch.rs:10:69
+   |
+LL |     fn c(self: Pin<&Self>, f: &Foo, g: &Foo) -> (Pin<&Foo>, &Foo) { (self, f) }
+   |                    -          -                                     ^^^^^^^^^ function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+   |                    |          |
+   |                    |          let's call the lifetime of this reference `'1`
+   |                    let's call the lifetime of this reference `'2`
+
+error: lifetime may not live long enough
+  --> $DIR/arbitrary_self_types_pin_lifetime_mismatch.rs:15:58
+   |
+LL |     fn bar<'a>(self: Alias<&Self>, arg: &'a ()) -> &() { arg }
+   |            --  ---- has type `std::pin::Pin<&'1 Foo>`    ^^^ function was supposed to return data with lifetime `'1` but it is returning data with lifetime `'a`
+   |            |
+   |            lifetime `'a` defined here
+
+error: aborting due to 3 previous errors
+
diff --git a/src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.rs b/src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.rs
new file mode 100644
index 0000000..fc5f942
--- /dev/null
+++ b/src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.rs
@@ -0,0 +1,18 @@
+// compile-fail
+
+use std::pin::Pin;
+
+struct Foo;
+
+impl Foo {
+    fn a(self: Pin<&Foo>, f: &Foo) -> &Foo { f } //~ ERROR E0623
+
+    fn c(self: Pin<&Self>, f: &Foo, g: &Foo) -> (Pin<&Foo>, &Foo) { (self, f) } //~ ERROR E0623
+}
+
+type Alias<T> = Pin<T>;
+impl Foo {
+    fn bar<'a>(self: Alias<&Self>, arg: &'a ()) -> &() { arg } //~ ERROR E0623
+}
+
+fn main() {}
diff --git a/src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.stderr b/src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.stderr
new file mode 100644
index 0000000..3296e14
--- /dev/null
+++ b/src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.stderr
@@ -0,0 +1,26 @@
+error[E0623]: lifetime mismatch
+  --> $DIR/arbitrary_self_types_pin_lifetime_mismatch.rs:8:46
+   |
+LL |     fn a(self: Pin<&Foo>, f: &Foo) -> &Foo { f }
+   |                              ----     ----   ^ ...but data from `f` is returned here
+   |                              |
+   |                              this parameter and the return type are declared with different lifetimes...
+
+error[E0623]: lifetime mismatch
+  --> $DIR/arbitrary_self_types_pin_lifetime_mismatch.rs:10:76
+   |
+LL |     fn c(self: Pin<&Self>, f: &Foo, g: &Foo) -> (Pin<&Foo>, &Foo) { (self, f) }
+   |                               ----              -----------------          ^ ...but data from `f` is returned here
+   |                               |
+   |                               this parameter and the return type are declared with different lifetimes...
+
+error[E0623]: lifetime mismatch
+  --> $DIR/arbitrary_self_types_pin_lifetime_mismatch.rs:15:58
+   |
+LL |     fn bar<'a>(self: Alias<&Self>, arg: &'a ()) -> &() { arg }
+   |                                         ------     ---   ^^^ ...but data from `arg` is returned here
+   |                                         |
+   |                                         this parameter and the return type are declared with different lifetimes...
+
+error: aborting due to 3 previous errors
+
diff --git a/src/test/ui/self/self_lifetime.rs b/src/test/ui/self/self_lifetime.rs
new file mode 100644
index 0000000..a3163ade
--- /dev/null
+++ b/src/test/ui/self/self_lifetime.rs
@@ -0,0 +1,13 @@
+// compile-pass
+
+struct Foo<'a>(&'a ());
+impl<'a> Foo<'a> {
+    fn foo<'b>(self: &'b Foo<'a>) -> &() { self.0 }
+}
+
+type Alias = Foo<'static>;
+impl Alias {
+    fn bar<'a>(self: &Alias, arg: &'a ()) -> &() { arg }
+}
+
+fn main() {}