Skip to content

Commit

Permalink
Fix lifetime elision
Browse files Browse the repository at this point in the history
  struct Concrete(u32);

  impl Concrete {
      fn m(self: &Box<Self>) -> &u32 {
          &self.0
      }
  }

resulted in a confusing error.

  impl Concrete {
      fn n(self: &Box<&Self>) -> &u32 {
          &self.0
      }
  }

resulted in no error or warning, despite apparent ambiguity over the elided lifetime.

Fixes #117715
  • Loading branch information
adetaylor committed Nov 16, 2023
1 parent fd0a331 commit a15cf8d
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 9 deletions.
24 changes: 16 additions & 8 deletions compiler/rustc_resolve/src/late.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2052,13 +2052,15 @@ 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>
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 @@ -2082,6 +2084,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
r: &'r Resolver<'a, 'tcx>,
impl_self: Option<Res>,
lifetime: Set1<LifetimeRes>,
self_found: bool,
}

impl SelfVisitor<'_, '_, '_> {
Expand All @@ -2105,7 +2108,11 @@ 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) {
if self.is_self_ty(ty) {
trace!("SelfVisitor found Self");
self.self_found = true;
}
if let TyKind::Ref(lt, _) = ty.kind {
let lt_id = if let Some(lt) = lt {
lt.id
} else {
Expand Down Expand Up @@ -2146,10 +2153,11 @@ 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 =
SelfVisitor { r: self.r, impl_self, lifetime: Set1::Empty, self_found: false };
visitor.visit_ty(ty);
trace!("SelfVisitor found={:?}", visitor.lifetime);
visitor.lifetime
trace!("SelfVisitor found={:?}, self_found={:?}", visitor.lifetime, visitor.self_found);
if visitor.self_found { visitor.lifetime } else { Set1::Empty }
}

/// Searches the current set of local scopes for labels. Returns the `NodeId` of the resolved
Expand Down
24 changes: 24 additions & 0 deletions tests/ui/self/elision/ref-self-multi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#![feature(arbitrary_self_types)]
#![allow(non_snake_case)]

use std::marker::PhantomData;
use std::ops::Deref;
use std::pin::Pin;

struct Struct { }

struct Wrap<T, P>(T, PhantomData<P>);

impl<T, P> Deref for Wrap<T, P> {
type Target = T;
fn deref(&self) -> &T { &self.0 }
}

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

fn main() { }
15 changes: 15 additions & 0 deletions tests/ui/self/elision/ref-self-multi.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
error[E0106]: missing lifetime specifier
--> $DIR/ref-self-multi.rs:18:56
|
LL | fn ref_box_ref_Self(self: &Box<&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 ref_box_ref_Self<'a>(self: &'a Box<&'a Self>, f: &'a u32) -> &'a u32 {
| ++++ ++ ++ ++ ++

error: aborting due to previous error

For more information about this error, try `rustc --explain E0106`.
5 changes: 5 additions & 0 deletions tests/ui/self/elision/ref-self.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ impl Struct {
f
//~^ ERROR lifetime may not live long enough
}

fn ref_box_Self(self: &Box<Self>, f: &u32) -> &u32 {
f
//~^ ERROR lifetime may not live long enough
}
}

fn main() { }
17 changes: 16 additions & 1 deletion tests/ui/self/elision/ref-self.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -103,5 +103,20 @@ help: consider introducing a named lifetime parameter and update trait if needed
LL | fn wrap_ref_Self_Self<'a>(self: Wrap<&'a Self, Self>, f: &'a u8) -> &u8 {
| ++++ ++ ++

error: aborting due to 7 previous errors
error: lifetime may not live long enough
--> $DIR/ref-self.rs:58:9
|
LL | fn ref_box_Self(self: &Box<Self>, f: &u32) -> &u32 {
| - - let's call the lifetime of this reference `'1`
| |
| let's call the lifetime of this reference `'2`
LL | f
| ^ method was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
|
help: consider introducing a named lifetime parameter and update trait if needed
|
LL | fn ref_box_Self<'a>(self: &'a Box<Self>, f: &'a u32) -> &u32 {
| ++++ ++ ++

error: aborting due to 8 previous errors

0 comments on commit a15cf8d

Please sign in to comment.