Fix ambiguous cases of multiple & in elided self lifetimes by adetaylor · Pull Request #117967 · rust-lang/rust (original) (raw)

This change proposes simpler rules to identify the lifetime on self parameters which may be used to elide a return type lifetime.

The old rules

(copied from this comment)

Most of the code can be found in late.rs and acts on AST types. The function resolve_fn_params, in the success case, returns a single lifetime which can be used to elide the lifetime of return types.

Here's how:

(In practice the code does extra work to assemble good diagnostic information, so it's not quite laid out like the above.)

self search rules

This is primarily handled in find_lifetime_for_self , and is described slightly here already. The code:

  1. Recursively walks the type of the self parameter (there's some complexity about resolving various special cases, but it's essentially just walking the type as far as I can see)
  2. Each time we find a reference anywhere in the type, if the direct referent is Self (either spelled Self or by some alias resolution which I don't fully understand), then we'll add that to a set of candidate lifetimes
  3. If there's exactly one such unique lifetime candidate found, we return this lifetime.

Regular parameter search rules

  1. Find all the lifetimes in each parameter, including implicit, explicit etc.
  2. If there's exactly one parameter containing lifetimes, and if that parameter contains exactly one (unique) lifetime, and if we didn't find a self lifetime parameter already, we'll return this lifetime.

The new rules

There are no changes to the "regular parameter search rules" or to the overall flow, only to the self search rules which are now:

  1. Recursively walks the type of the self parameter, searching for lifetimes of reference types whose referent contains Self.1
  2. Keep a record of:
    • Whether 0, 1 or n unique lifetimes are found on references encountered during the walk
  3. If no lifetime was found, we don't return a lifetime. (This means other parameters' lifetimes may be used for return type lifetime elision).
  4. If there's one lifetime found, we return the lifetime.
  5. If multiple lifetimes were found, we abort elision entirely (other parameters' lifetimes won't be used).

Examples that were accepted before and will now be rejected

fn a(self: &Box<&Self>) -> &u32 fn b(self: &Pin<&mut Self>) -> &String fn c(self: &mut &Self) -> Option<&Self> fn d(self: &mut &Box, arg: &usize) -> &usize // previously used the lt from arg

Examples that change the elided lifetime

fn e(self: &mut Box, arg: &usize) -> &usize // ^ new ^ previous

Examples that were rejected before and will now be accepted

fn f(self: &Box) -> &u32


edit: old PR description:

struct Concrete(u32);

impl Concrete { fn m(self: &Box) -> &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

Footnotes

  1. this prevents us from considering lifetimes from inside of the self-type