Only ban duplication across parameters.

This commit is contained in:
Camille GILLOT 2022-10-27 17:24:38 +00:00
parent 47704bbcc0
commit cb1e7d9676
3 changed files with 71 additions and 32 deletions

View File

@ -30,6 +30,7 @@ use rustc_span::{BytePos, Span};
use smallvec::{smallvec, SmallVec};
use rustc_span::source_map::{respan, Spanned};
use std::assert_matches::debug_assert_matches;
use std::collections::{hash_map::Entry, BTreeSet};
use std::mem::{replace, take};
@ -1852,12 +1853,25 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
has_self: bool,
inputs: impl Iterator<Item = (Option<&'ast Pat>, &'ast Ty)>,
) -> Result<LifetimeRes, (Vec<MissingLifetime>, Vec<ElisionFnParameter>)> {
let outer_candidates =
replace(&mut self.lifetime_elision_candidates, Some(Default::default()));
enum Elision {
/// We have not found any candidate.
None,
/// We have a candidate bound to `self`.
Self_(LifetimeRes),
/// We have a candidate bound to a parameter.
Param(LifetimeRes),
/// We failed elision.
Err,
}
let mut elision_lifetime = None;
let mut lifetime_count = 0;
// Save elision state to reinstate it later.
let outer_candidates = self.lifetime_elision_candidates.take();
// Result of elision.
let mut elision_lifetime = Elision::None;
// Information for diagnostics.
let mut parameter_info = Vec::new();
let mut all_candidates = Vec::new();
let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())];
for (index, (pat, ty)) in inputs.enumerate() {
@ -1867,12 +1881,17 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
this.resolve_pattern(pat, PatternSource::FnParam, &mut bindings);
}
});
self.visit_ty(ty);
if let Some(ref candidates) = self.lifetime_elision_candidates {
let new_count = candidates.len();
let local_count = new_count - lifetime_count;
if local_count != 0 {
// Record elision candidates only for this parameter.
debug_assert_matches!(self.lifetime_elision_candidates, None);
self.lifetime_elision_candidates = Some(Default::default());
self.visit_ty(ty);
let local_candidates = self.lifetime_elision_candidates.take();
if let Some(candidates) = local_candidates {
let distinct: FxHashSet<_> = candidates.iter().map(|(res, _)| *res).collect();
let lifetime_count = distinct.len();
if lifetime_count != 0 {
parameter_info.push(ElisionFnParameter {
index,
ident: if let Some(pat) = pat && let PatKind::Ident(_, ident, _) = pat.kind {
@ -1880,48 +1899,64 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
} else {
None
},
lifetime_count: local_count,
lifetime_count,
span: ty.span,
});
all_candidates.extend(candidates.into_iter().filter_map(|(_, candidate)| {
match candidate {
LifetimeElisionCandidate::Ignore | LifetimeElisionCandidate::Named => {
None
}
LifetimeElisionCandidate::Missing(missing) => Some(missing),
}
}));
}
let mut distinct_iter = distinct.into_iter();
if let Some(res) = distinct_iter.next() {
match elision_lifetime {
// We are the first parameter to bind lifetimes.
Elision::None => {
if distinct_iter.next().is_none() {
// We have a single lifetime => success.
elision_lifetime = Elision::Param(res)
} else {
// We have have multiple lifetimes => error.
elision_lifetime = Elision::Err;
}
}
// We have 2 parameters that bind lifetimes => error.
Elision::Param(_) => elision_lifetime = Elision::Err,
// `self` elision takes precedence over everything else.
Elision::Self_(_) | Elision::Err => {}
}
}
lifetime_count = new_count;
}
// 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 = Some(lifetime);
self.lifetime_elision_candidates = None;
// We found `self` elision.
elision_lifetime = Elision::Self_(lifetime);
} else {
self.lifetime_elision_candidates = Some(Default::default());
lifetime_count = 0;
// We do not have `self` elision: disregard the `Elision::Param` that we may
// have found.
elision_lifetime = Elision::None;
}
}
debug!("(resolving function / closure) recorded parameter");
}
let all_candidates = replace(&mut self.lifetime_elision_candidates, outer_candidates);
debug!(?all_candidates);
// Reinstate elision state.
debug_assert_matches!(self.lifetime_elision_candidates, None);
self.lifetime_elision_candidates = outer_candidates;
if let Some(res) = elision_lifetime {
if let Elision::Param(res) | Elision::Self_(res) = elision_lifetime {
return Ok(res);
}
// We do not have a `self` candidate, look at the full list.
let all_candidates = all_candidates.unwrap();
if let [(res, _)] = &all_candidates[..] {
Ok(*res)
} else {
let all_candidates = all_candidates
.into_iter()
.filter_map(|(_, candidate)| match candidate {
LifetimeElisionCandidate::Ignore | LifetimeElisionCandidate::Named => None,
LifetimeElisionCandidate::Missing(missing) => Some(missing),
})
.collect();
Err((all_candidates, parameter_info))
}
// We do not have a candidate.
Err((all_candidates, parameter_info))
}
/// List all the lifetimes that appear in the provided type.

View File

@ -7,6 +7,7 @@
//! Type-relative name resolution (methods, fields, associated items) happens in `rustc_hir_analysis`.
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![feature(assert_matches)]
#![feature(box_patterns)]
#![feature(drain_filter)]
#![feature(if_let_guard)]

View File

@ -45,4 +45,7 @@ fn k<'a, T: WithLifetime<'a>>(_x: T::Output) -> &isize {
fn l<'a>(_: &'a str, _: &'a str) -> &str { "" }
//~^ ERROR missing lifetime specifier
// This is ok because both `'a` are for the same parameter.
fn m<'a>(_: &'a Foo<'a>) -> &str { "" }
fn main() {}