Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix ambiguous cases of multiple & in elided self lifetimes #117967

Merged
merged 7 commits into from
Jul 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 56 additions & 22 deletions compiler/rustc_resolve/src/late.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2132,13 +2132,17 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
// Handle `self` specially.
if index == 0 && has_self {
let self_lifetime = self.find_lifetime_for_self(ty);
if let Set1::One(lifetime) = self_lifetime {
elision_lifetime = match self_lifetime {
// We found `self` elision.
elision_lifetime = Elision::Self_(lifetime);
} else {
Set1::One(lifetime) => Elision::Self_(lifetime),
// `self` itself had ambiguous lifetimes, e.g.
// &Box<&Self>. In this case we won't consider
// taking an alternative parameter lifetime; just avoid elision
// entirely.
Set1::Many => Elision::Err,
// We do not have `self` elision: disregard the `Elision::Param` that we may
// have found.
elision_lifetime = Elision::None;
Set1::Empty => Elision::None,
}
}
debug!("(resolving function / closure) recorded parameter");
Expand All @@ -2158,15 +2162,55 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {

/// List all the lifetimes that appear in the provided type.
fn find_lifetime_for_self(&self, ty: &'ast Ty) -> Set1<LifetimeRes> {
struct SelfVisitor<'r, 'a, 'tcx> {
/// Visits a type to find all the &references, and determines the
/// set of lifetimes for all of those references where the referent
/// contains Self.
struct FindReferenceVisitor<'r, 'a, 'tcx> {
r: &'r Resolver<'a, 'tcx>,
impl_self: Option<Res>,
lifetime: Set1<LifetimeRes>,
}

impl<'a> Visitor<'a> for FindReferenceVisitor<'_, '_, '_> {
fn visit_ty(&mut self, ty: &'a Ty) {
trace!("FindReferenceVisitor considering ty={:?}", ty);
if let TyKind::Ref(lt, _) = ty.kind {
// See if anything inside the &thing contains Self
let mut visitor =
SelfVisitor { r: self.r, impl_self: self.impl_self, self_found: false };
visitor.visit_ty(ty);
trace!("FindReferenceVisitor: SelfVisitor self_found={:?}", visitor.self_found);
if visitor.self_found {
let lt_id = if let Some(lt) = lt {
lt.id
} else {
let res = self.r.lifetimes_res_map[&ty.id];
let LifetimeRes::ElidedAnchor { start, .. } = res else { bug!() };
start
};
let lt_res = self.r.lifetimes_res_map[&lt_id];
trace!("FindReferenceVisitor inserting res={:?}", lt_res);
self.lifetime.insert(lt_res);
}
}
visit::walk_ty(self, ty)
}

// A type may have an expression as a const generic argument.
// We do not want to recurse into those.
fn visit_expr(&mut self, _: &'a Expr) {}
}

/// Visitor which checks the referent of a &Thing to see if the
/// Thing contains Self
struct SelfVisitor<'r, 'a, 'tcx> {
r: &'r Resolver<'a, 'tcx>,
impl_self: Option<Res>,
self_found: bool,
}

impl SelfVisitor<'_, '_, '_> {
// Look for `self: &'a Self` - also desugared from `&'a self`,
// and if that matches, use it for elision and return early.
// Look for `self: &'a Self` - also desugared from `&'a self`
fn is_self_ty(&self, ty: &Ty) -> bool {
match ty.kind {
TyKind::ImplicitSelf => true,
Expand All @@ -2185,19 +2229,9 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
impl<'a> Visitor<'a> for SelfVisitor<'_, '_, '_> {
fn visit_ty(&mut self, ty: &'a Ty) {
trace!("SelfVisitor considering ty={:?}", ty);
if let TyKind::Ref(lt, ref mt) = ty.kind
&& self.is_self_ty(&mt.ty)
{
let lt_id = if let Some(lt) = lt {
lt.id
} else {
let res = self.r.lifetimes_res_map[&ty.id];
let LifetimeRes::ElidedAnchor { start, .. } = res else { bug!() };
start
};
let lt_res = self.r.lifetimes_res_map[&lt_id];
trace!("SelfVisitor inserting res={:?}", lt_res);
self.lifetime.insert(lt_res);
if self.is_self_ty(ty) {
trace!("SelfVisitor found Self");
self.self_found = true;
}
visit::walk_ty(self, ty)
}
Expand Down Expand Up @@ -2228,9 +2262,9 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
Res::Def(DefKind::Struct | DefKind::Union | DefKind::Enum, _,) | Res::PrimTy(_)
)
});
let mut visitor = SelfVisitor { r: self.r, impl_self, lifetime: Set1::Empty };
let mut visitor = FindReferenceVisitor { r: self.r, impl_self, lifetime: Set1::Empty };
visitor.visit_ty(ty);
trace!("SelfVisitor found={:?}", visitor.lifetime);
trace!("FindReferenceVisitor found={:?}", visitor.lifetime);
visitor.lifetime
}

Expand Down
4 changes: 2 additions & 2 deletions tests/crashes/122903-1.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//@ known-bug: #122903
impl Struct {
async fn box_box_ref_Struct(
self: Box<Box<Self, impl FnMut(&mut Box<Box<Self, impl FnMut(&mut Self)>>)>>,
fn box_box_ref_Struct(
self: impl FnMut(Box<impl FnMut(&mut Self)>),
) -> &u32 {
f
}
Expand Down
9 changes: 0 additions & 9 deletions tests/crashes/122903-2.rs

This file was deleted.

22 changes: 22 additions & 0 deletions tests/ui/self/elision/ignore-non-reference-lifetimes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//@ check-pass
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this test is now instead testing the "no-recurse-into-self" rule, isn't it?


struct Foo<'a>(&'a str);

impl<'b> Foo<'b> {
fn a<'a>(self: Self, a: &'a str) -> &str {
a
}
fn b<'a>(self: Foo<'b>, a: &'a str) -> &str {
a
}
}

struct Foo2<'a>(&'a u32);
impl<'a> Foo2<'a> {
fn foo(self: &Self) -> &u32 { self.0 } // ok
fn bar(self: &Foo2<'a>) -> &u32 { self.0 } // ok (do not look into `Foo`)
fn baz2(self: Self, arg: &u32) -> &u32 { arg } // use lt from `arg`
fn baz3(self: Foo2<'a>, arg: &u32) -> &u32 { arg } // use lt from `arg`
}

fn main() {}
6 changes: 5 additions & 1 deletion tests/ui/self/elision/multiple-ref-self-async.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
//@ check-pass
//@ edition:2018

#![feature(arbitrary_self_types)]
Expand All @@ -21,22 +20,27 @@ impl Struct {
// Test using multiple `&Self`:

async fn wrap_ref_Self_ref_Self(self: Wrap<&Self, &Self>, f: &u8) -> &u8 {
//~^ ERROR missing lifetime specifier
f
}

async fn box_wrap_ref_Self_ref_Self(self: Box<Wrap<&Self, &Self>>, f: &u32) -> &u32 {
//~^ ERROR missing lifetime specifier
f
}

async fn pin_wrap_ref_Self_ref_Self(self: Pin<Wrap<&Self, &Self>>, f: &u32) -> &u32 {
//~^ ERROR missing lifetime specifier
f
}

async fn box_box_wrap_ref_Self_ref_Self(self: Box<Box<Wrap<&Self, &Self>>>, f: &u32) -> &u32 {
//~^ ERROR missing lifetime specifier
f
}

async fn box_pin_wrap_ref_Self_ref_Self(self: Box<Pin<Wrap<&Self, &Self>>>, f: &u32) -> &u32 {
//~^ ERROR missing lifetime specifier
f
}
}
Expand Down
63 changes: 63 additions & 0 deletions tests/ui/self/elision/multiple-ref-self-async.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
error[E0106]: missing lifetime specifier
--> $DIR/multiple-ref-self-async.rs:22:74
|
LL | async fn wrap_ref_Self_ref_Self(self: Wrap<&Self, &Self>, f: &u8) -> &u8 {
| ------------------ --- ^ expected named lifetime parameter
|
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from one of `self`'s 2 lifetimes or `f`
help: consider introducing a named lifetime parameter
|
LL | async fn wrap_ref_Self_ref_Self<'a>(self: Wrap<&'a Self, &'a Self>, f: &'a u8) -> &'a u8 {
| ++++ ++ ++ ++ ++

error[E0106]: missing lifetime specifier
--> $DIR/multiple-ref-self-async.rs:27:84
|
LL | async fn box_wrap_ref_Self_ref_Self(self: Box<Wrap<&Self, &Self>>, f: &u32) -> &u32 {
| ----------------------- ---- ^ expected named lifetime parameter
|
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from one of `self`'s 2 lifetimes or `f`
help: consider introducing a named lifetime parameter
|
LL | async fn box_wrap_ref_Self_ref_Self<'a>(self: Box<Wrap<&'a Self, &'a Self>>, f: &'a u32) -> &'a u32 {
| ++++ ++ ++ ++ ++

error[E0106]: missing lifetime specifier
--> $DIR/multiple-ref-self-async.rs:32:84
|
LL | async fn pin_wrap_ref_Self_ref_Self(self: Pin<Wrap<&Self, &Self>>, f: &u32) -> &u32 {
| ----------------------- ---- ^ expected named lifetime parameter
|
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from one of `self`'s 2 lifetimes or `f`
help: consider introducing a named lifetime parameter
|
LL | async fn pin_wrap_ref_Self_ref_Self<'a>(self: Pin<Wrap<&'a Self, &'a Self>>, f: &'a u32) -> &'a u32 {
| ++++ ++ ++ ++ ++

error[E0106]: missing lifetime specifier
--> $DIR/multiple-ref-self-async.rs:37:93
|
LL | async fn box_box_wrap_ref_Self_ref_Self(self: Box<Box<Wrap<&Self, &Self>>>, f: &u32) -> &u32 {
| ---------------------------- ---- ^ expected named lifetime parameter
|
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from one of `self`'s 2 lifetimes or `f`
help: consider introducing a named lifetime parameter
|
LL | async fn box_box_wrap_ref_Self_ref_Self<'a>(self: Box<Box<Wrap<&'a Self, &'a Self>>>, f: &'a u32) -> &'a u32 {
| ++++ ++ ++ ++ ++

error[E0106]: missing lifetime specifier
--> $DIR/multiple-ref-self-async.rs:42:93
|
LL | async fn box_pin_wrap_ref_Self_ref_Self(self: Box<Pin<Wrap<&Self, &Self>>>, f: &u32) -> &u32 {
| ---------------------------- ---- ^ expected named lifetime parameter
|
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from one of `self`'s 2 lifetimes or `f`
help: consider introducing a named lifetime parameter
|
LL | async fn box_pin_wrap_ref_Self_ref_Self<'a>(self: Box<Pin<Wrap<&'a Self, &'a Self>>>, f: &'a u32) -> &'a u32 {
| ++++ ++ ++ ++ ++

error: aborting due to 5 previous errors

For more information about this error, try `rustc --explain E0106`.
7 changes: 5 additions & 2 deletions tests/ui/self/elision/multiple-ref-self.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
//@ check-pass

#![feature(arbitrary_self_types)]
#![allow(non_snake_case)]

Expand All @@ -20,22 +18,27 @@ impl Struct {
// Test using multiple `&Self`:

fn wrap_ref_Self_ref_Self(self: Wrap<&Self, &Self>, f: &u8) -> &u8 {
//~^ ERROR missing lifetime specifier
f
}

fn box_wrap_ref_Self_ref_Self(self: Box<Wrap<&Self, &Self>>, f: &u32) -> &u32 {
//~^ ERROR missing lifetime specifier
f
}

fn pin_wrap_ref_Self_ref_Self(self: Pin<Wrap<&Self, &Self>>, f: &u32) -> &u32 {
//~^ ERROR missing lifetime specifier
f
}

fn box_box_wrap_ref_Self_ref_Self(self: Box<Box<Wrap<&Self, &Self>>>, f: &u32) -> &u32 {
//~^ ERROR missing lifetime specifier
f
}

fn box_pin_wrap_ref_Self_ref_Self(self: Box<Pin<Wrap<&Self, &Self>>>, f: &u32) -> &u32 {
//~^ ERROR missing lifetime specifier
f
}
}
Expand Down
63 changes: 63 additions & 0 deletions tests/ui/self/elision/multiple-ref-self.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
error[E0106]: missing lifetime specifier
--> $DIR/multiple-ref-self.rs:20:68
|
LL | fn wrap_ref_Self_ref_Self(self: Wrap<&Self, &Self>, f: &u8) -> &u8 {
| ------------------ --- ^ expected named lifetime parameter
|
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from one of `self`'s 2 lifetimes or `f`
help: consider introducing a named lifetime parameter
|
LL | fn wrap_ref_Self_ref_Self<'a>(self: Wrap<&'a Self, &'a Self>, f: &'a u8) -> &'a u8 {
| ++++ ++ ++ ++ ++

error[E0106]: missing lifetime specifier
--> $DIR/multiple-ref-self.rs:25:78
|
LL | fn box_wrap_ref_Self_ref_Self(self: Box<Wrap<&Self, &Self>>, f: &u32) -> &u32 {
| ----------------------- ---- ^ expected named lifetime parameter
|
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from one of `self`'s 2 lifetimes or `f`
help: consider introducing a named lifetime parameter
|
LL | fn box_wrap_ref_Self_ref_Self<'a>(self: Box<Wrap<&'a Self, &'a Self>>, f: &'a u32) -> &'a u32 {
| ++++ ++ ++ ++ ++

error[E0106]: missing lifetime specifier
--> $DIR/multiple-ref-self.rs:30:78
|
LL | fn pin_wrap_ref_Self_ref_Self(self: Pin<Wrap<&Self, &Self>>, f: &u32) -> &u32 {
| ----------------------- ---- ^ expected named lifetime parameter
|
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from one of `self`'s 2 lifetimes or `f`
help: consider introducing a named lifetime parameter
|
LL | fn pin_wrap_ref_Self_ref_Self<'a>(self: Pin<Wrap<&'a Self, &'a Self>>, f: &'a u32) -> &'a u32 {
| ++++ ++ ++ ++ ++

error[E0106]: missing lifetime specifier
--> $DIR/multiple-ref-self.rs:35:87
|
LL | fn box_box_wrap_ref_Self_ref_Self(self: Box<Box<Wrap<&Self, &Self>>>, f: &u32) -> &u32 {
| ---------------------------- ---- ^ expected named lifetime parameter
|
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from one of `self`'s 2 lifetimes or `f`
help: consider introducing a named lifetime parameter
|
LL | fn box_box_wrap_ref_Self_ref_Self<'a>(self: Box<Box<Wrap<&'a Self, &'a Self>>>, f: &'a u32) -> &'a u32 {
| ++++ ++ ++ ++ ++

error[E0106]: missing lifetime specifier
--> $DIR/multiple-ref-self.rs:40:87
|
LL | fn box_pin_wrap_ref_Self_ref_Self(self: Box<Pin<Wrap<&Self, &Self>>>, f: &u32) -> &u32 {
| ---------------------------- ---- ^ expected named lifetime parameter
|
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from one of `self`'s 2 lifetimes or `f`
help: consider introducing a named lifetime parameter
|
LL | fn box_pin_wrap_ref_Self_ref_Self<'a>(self: Box<Pin<Wrap<&'a Self, &'a Self>>>, f: &'a u32) -> &'a u32 {
| ++++ ++ ++ ++ ++

error: aborting due to 5 previous errors

For more information about this error, try `rustc --explain E0106`.
17 changes: 17 additions & 0 deletions tests/ui/self/elision/no-shadow-pin-self.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use std::pin::Pin;
trait Trait {
fn method<'a>(self: Pin<&Self>, f: &'a u32) -> &'a u32 {
f
}
}

impl<P> Trait for Pin<P> {
// This should not hide `&Self`, which would cause this to compile.
fn method(self: Pin<&Self>, f: &u32) -> &u32 {
//~^ ERROR `impl` item signature doesn't match `trait`
f
//~^ ERROR lifetime may not live long enough
}
}

fn main() {}
Loading
Loading