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() {}