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:
- If the first parameter is called self then we search that parameter using "
self
search rules", below - If no unique applicable lifetime was found, search all other parameters using "regular parameter search rules", below
(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:
- 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) - Each time we find a reference anywhere in the type, if the direct referent is
Self
(either spelledSelf
or by some alias resolution which I don't fully understand), then we'll add that to a set of candidate lifetimes - If there's exactly one such unique lifetime candidate found, we return this lifetime.
Regular parameter search rules
- Find all the lifetimes in each parameter, including implicit, explicit etc.
- 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:
- Recursively walks the type of the
self
parameter, searching for lifetimes of reference types whose referent containsSelf
.1 - Keep a record of:
- Whether 0, 1 or n unique lifetimes are found on references encountered during the walk
- If no lifetime was found, we don't return a lifetime. (This means other parameters' lifetimes may be used for return type lifetime elision).
- If there's one lifetime found, we return the lifetime.
- 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
- this prevents us from considering lifetimes from inside of the self-type ↩