From 46c752a8345c13a2dd856743872fcf3eb0974582 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Mon, 27 May 2019 01:43:12 +0900 Subject: [PATCH] Allow lifetime elision in `Pin<&(mut) Self>` --- src/librustc/middle/resolve_lifetime.rs | 29 ++++++++- .../self/arbitrary_self_types_pin_lifetime.rs | 60 +++++++++++++++++++ ...rary_self_types_pin_lifetime_impl_trait.rs | 13 ++++ ..._self_types_pin_lifetime_impl_trait.stderr | 20 +++++++ ...itrary_self_types_pin_lifetime_mismatch.rs | 13 ++++ ...ry_self_types_pin_lifetime_mismatch.stderr | 18 ++++++ src/test/ui/self/self_lifetime.rs | 13 ++++ 7 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/self/arbitrary_self_types_pin_lifetime.rs create mode 100644 src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.rs create mode 100644 src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.stderr create mode 100644 src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.rs create mode 100644 src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.stderr create mode 100644 src/test/ui/self/self_lifetime.rs diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index 775da1de313fa..941ea079b2c81 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -2149,7 +2149,34 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { false }; - if let hir::TyKind::Rptr(lifetime_ref, ref mt) = inputs[0].node { + let mut self_arg = &inputs[0].node; + + // Apply `self: &(mut) Self` elision rules even if nested in `Pin`. + loop { + if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) = *self_arg { + if let Res::Def(DefKind::Struct, def_id) = path.res { + if self.tcx.lang_items().pin_type() == Some(def_id) { + if let Some(args) = path + .segments + .last() + .and_then(|segment| segment.args.as_ref()) + { + if args.args.len() == 1 { + if let GenericArg::Type(ty) = &args.args[0] { + self_arg = &ty.node; + // Keep dereferencing `self_arg` until we get to non-`Pin` + // types. + continue; + } + } + } + } + } + } + break; + } + + if let hir::TyKind::Rptr(lifetime_ref, ref mt) = *self_arg { 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) { 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 0000000000000..668aaf7166a0f --- /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>> { self } + + fn pin_ref_impl_trait(self: Pin<&Self>) -> impl Clone + '_ { self } + + fn b(self: Pin<&Foo>, f: &Foo) -> Pin<&Foo> { self } +} + +type Alias = Pin; +impl Foo { + fn bar<'a>(self: Alias<&Self>, arg: &'a ()) -> &() { arg } +} + +struct Bar { + field1: T, + field2: U, +} + +impl Bar { + 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>; +} + +struct Baz(Vec); + +impl AsyncBufRead for Baz { + fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) + -> Poll> + { + 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.rs b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.rs new file mode 100644 index 0000000000000..ad8959727cbee --- /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 0000000000000..5118280e7ec0c --- /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.rs b/src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.rs new file mode 100644 index 0000000000000..3ed5e6bdd7211 --- /dev/null +++ b/src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.rs @@ -0,0 +1,13 @@ +// 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 +} + +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 0000000000000..6e345b03056e9 --- /dev/null +++ b/src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.stderr @@ -0,0 +1,18 @@ +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: aborting due to 2 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 0000000000000..a3163ade0404f --- /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() {}