Skip to content

Commit

Permalink
Auto merge of #62209 - nikomatsakis:arbitrary_self_types-lifetime-eli…
Browse files Browse the repository at this point in the history
…sion-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
  • Loading branch information
bors committed Jun 29, 2019
2 parents 09a353c + 83dc1b5 commit 9d98f8f
Show file tree
Hide file tree
Showing 9 changed files with 254 additions and 33 deletions.
95 changes: 62 additions & 33 deletions src/librustc/middle/resolve_lifetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2126,48 +2126,77 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
// 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;
}

// 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
struct SelfVisitor<'a> {
map: &'a NamedRegionMap,
impl_self: Option<&'a hir::TyKind>,
lifetime: Set1<Region>,
}

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
}
}

false
};
impl<'a> Visitor<'a> for SelfVisitor<'a> {
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'a> {
NestedVisitorMap::None
}

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;
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,
};
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;
}
}

// Second, if there was exactly one lifetime (either a substitution or a
Expand Down
60 changes: 60 additions & 0 deletions src/test/ui/self/arbitrary_self_types_pin_lifetime.rs
Original file line number Diff line number Diff line change
@@ -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() };
}
Original file line number Diff line number Diff line change
@@ -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

13 changes: 13 additions & 0 deletions src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.rs
Original file line number Diff line number Diff line change
@@ -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() };
}
Original file line number Diff line number Diff line change
@@ -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

Original file line number Diff line number Diff line change
@@ -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

18 changes: 18 additions & 0 deletions src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.rs
Original file line number Diff line number Diff line change
@@ -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() {}
26 changes: 26 additions & 0 deletions src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.stderr
Original file line number Diff line number Diff line change
@@ -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

13 changes: 13 additions & 0 deletions src/test/ui/self/self_lifetime.rs
Original file line number Diff line number Diff line change
@@ -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() {}

0 comments on commit 9d98f8f

Please sign in to comment.