Skip to content

Commit 5e97720

Browse files
committed
Auto merge of #103450 - cjgillot:elision-nodedup, r=Mark-Simulacrum
Do not consider repeated lifetime params for elision. Fixes #103330
2 parents 68c836a + cb1e7d9 commit 5e97720

File tree

3 files changed

+88
-35
lines changed

3 files changed

+88
-35
lines changed

compiler/rustc_resolve/src/late.rs

+69-34
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ use rustc_span::{BytePos, Span};
3030
use smallvec::{smallvec, SmallVec};
3131

3232
use rustc_span::source_map::{respan, Spanned};
33+
use std::assert_matches::debug_assert_matches;
3334
use std::collections::{hash_map::Entry, BTreeSet};
3435
use std::mem::{replace, take};
3536

@@ -568,7 +569,7 @@ struct LateResolutionVisitor<'a, 'b, 'ast> {
568569
/// They will be used to determine the correct lifetime for the fn return type.
569570
/// The `LifetimeElisionCandidate` is used for diagnostics, to suggest introducing named
570571
/// lifetimes.
571-
lifetime_elision_candidates: Option<FxIndexMap<LifetimeRes, LifetimeElisionCandidate>>,
572+
lifetime_elision_candidates: Option<Vec<(LifetimeRes, LifetimeElisionCandidate)>>,
572573

573574
/// The trait that the current context can refer to.
574575
current_trait_ref: Option<(Module<'a>, TraitRef)>,
@@ -1802,7 +1803,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
18021803
match res {
18031804
LifetimeRes::Param { .. } | LifetimeRes::Fresh { .. } | LifetimeRes::Static => {
18041805
if let Some(ref mut candidates) = self.lifetime_elision_candidates {
1805-
candidates.insert(res, candidate);
1806+
candidates.push((res, candidate));
18061807
}
18071808
}
18081809
LifetimeRes::Infer | LifetimeRes::Error | LifetimeRes::ElidedAnchor { .. } => {}
@@ -1855,12 +1856,25 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
18551856
has_self: bool,
18561857
inputs: impl Iterator<Item = (Option<&'ast Pat>, &'ast Ty)>,
18571858
) -> Result<LifetimeRes, (Vec<MissingLifetime>, Vec<ElisionFnParameter>)> {
1858-
let outer_candidates =
1859-
replace(&mut self.lifetime_elision_candidates, Some(Default::default()));
1859+
enum Elision {
1860+
/// We have not found any candidate.
1861+
None,
1862+
/// We have a candidate bound to `self`.
1863+
Self_(LifetimeRes),
1864+
/// We have a candidate bound to a parameter.
1865+
Param(LifetimeRes),
1866+
/// We failed elision.
1867+
Err,
1868+
}
18601869

1861-
let mut elision_lifetime = None;
1862-
let mut lifetime_count = 0;
1870+
// Save elision state to reinstate it later.
1871+
let outer_candidates = self.lifetime_elision_candidates.take();
1872+
1873+
// Result of elision.
1874+
let mut elision_lifetime = Elision::None;
1875+
// Information for diagnostics.
18631876
let mut parameter_info = Vec::new();
1877+
let mut all_candidates = Vec::new();
18641878

18651879
let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())];
18661880
for (index, (pat, ty)) in inputs.enumerate() {
@@ -1870,61 +1884,82 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
18701884
this.resolve_pattern(pat, PatternSource::FnParam, &mut bindings);
18711885
}
18721886
});
1887+
1888+
// Record elision candidates only for this parameter.
1889+
debug_assert_matches!(self.lifetime_elision_candidates, None);
1890+
self.lifetime_elision_candidates = Some(Default::default());
18731891
self.visit_ty(ty);
1892+
let local_candidates = self.lifetime_elision_candidates.take();
18741893

1875-
if let Some(ref candidates) = self.lifetime_elision_candidates {
1876-
let new_count = candidates.len();
1877-
let local_count = new_count - lifetime_count;
1878-
if local_count != 0 {
1894+
if let Some(candidates) = local_candidates {
1895+
let distinct: FxHashSet<_> = candidates.iter().map(|(res, _)| *res).collect();
1896+
let lifetime_count = distinct.len();
1897+
if lifetime_count != 0 {
18791898
parameter_info.push(ElisionFnParameter {
18801899
index,
18811900
ident: if let Some(pat) = pat && let PatKind::Ident(_, ident, _) = pat.kind {
18821901
Some(ident)
18831902
} else {
18841903
None
18851904
},
1886-
lifetime_count: local_count,
1905+
lifetime_count,
18871906
span: ty.span,
18881907
});
1908+
all_candidates.extend(candidates.into_iter().filter_map(|(_, candidate)| {
1909+
match candidate {
1910+
LifetimeElisionCandidate::Ignore | LifetimeElisionCandidate::Named => {
1911+
None
1912+
}
1913+
LifetimeElisionCandidate::Missing(missing) => Some(missing),
1914+
}
1915+
}));
1916+
}
1917+
let mut distinct_iter = distinct.into_iter();
1918+
if let Some(res) = distinct_iter.next() {
1919+
match elision_lifetime {
1920+
// We are the first parameter to bind lifetimes.
1921+
Elision::None => {
1922+
if distinct_iter.next().is_none() {
1923+
// We have a single lifetime => success.
1924+
elision_lifetime = Elision::Param(res)
1925+
} else {
1926+
// We have have multiple lifetimes => error.
1927+
elision_lifetime = Elision::Err;
1928+
}
1929+
}
1930+
// We have 2 parameters that bind lifetimes => error.
1931+
Elision::Param(_) => elision_lifetime = Elision::Err,
1932+
// `self` elision takes precedence over everything else.
1933+
Elision::Self_(_) | Elision::Err => {}
1934+
}
18891935
}
1890-
lifetime_count = new_count;
18911936
}
18921937

18931938
// Handle `self` specially.
18941939
if index == 0 && has_self {
18951940
let self_lifetime = self.find_lifetime_for_self(ty);
18961941
if let Set1::One(lifetime) = self_lifetime {
1897-
elision_lifetime = Some(lifetime);
1898-
self.lifetime_elision_candidates = None;
1942+
// We found `self` elision.
1943+
elision_lifetime = Elision::Self_(lifetime);
18991944
} else {
1900-
self.lifetime_elision_candidates = Some(Default::default());
1901-
lifetime_count = 0;
1945+
// We do not have `self` elision: disregard the `Elision::Param` that we may
1946+
// have found.
1947+
elision_lifetime = Elision::None;
19021948
}
19031949
}
19041950
debug!("(resolving function / closure) recorded parameter");
19051951
}
19061952

1907-
let all_candidates = replace(&mut self.lifetime_elision_candidates, outer_candidates);
1908-
debug!(?all_candidates);
1953+
// Reinstate elision state.
1954+
debug_assert_matches!(self.lifetime_elision_candidates, None);
1955+
self.lifetime_elision_candidates = outer_candidates;
19091956

1910-
if let Some(res) = elision_lifetime {
1957+
if let Elision::Param(res) | Elision::Self_(res) = elision_lifetime {
19111958
return Ok(res);
19121959
}
19131960

1914-
// We do not have a `self` candidate, look at the full list.
1915-
let all_candidates = all_candidates.unwrap();
1916-
if all_candidates.len() == 1 {
1917-
Ok(*all_candidates.first().unwrap().0)
1918-
} else {
1919-
let all_candidates = all_candidates
1920-
.into_iter()
1921-
.filter_map(|(_, candidate)| match candidate {
1922-
LifetimeElisionCandidate::Ignore | LifetimeElisionCandidate::Named => None,
1923-
LifetimeElisionCandidate::Missing(missing) => Some(missing),
1924-
})
1925-
.collect();
1926-
Err((all_candidates, parameter_info))
1927-
}
1961+
// We do not have a candidate.
1962+
Err((all_candidates, parameter_info))
19281963
}
19291964

19301965
/// List all the lifetimes that appear in the provided type.
@@ -2394,7 +2429,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
23942429
// Do not account for the parameters we just bound for function lifetime elision.
23952430
if let Some(ref mut candidates) = self.lifetime_elision_candidates {
23962431
for (_, res) in function_lifetime_rib.bindings.values() {
2397-
candidates.remove(res);
2432+
candidates.retain(|(r, _)| r != res);
23982433
}
23992434
}
24002435

src/test/ui/lifetimes/lifetime-elision-return-type-requires-explicit-lifetime.rs

+6
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,10 @@ fn k<'a, T: WithLifetime<'a>>(_x: T::Output) -> &isize {
4242
panic!()
4343
}
4444

45+
fn l<'a>(_: &'a str, _: &'a str) -> &str { "" }
46+
//~^ ERROR missing lifetime specifier
47+
48+
// This is ok because both `'a` are for the same parameter.
49+
fn m<'a>(_: &'a Foo<'a>) -> &str { "" }
50+
4551
fn main() {}

src/test/ui/lifetimes/lifetime-elision-return-type-requires-explicit-lifetime.stderr

+13-1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,18 @@ help: consider using the `'a` lifetime
7070
LL | fn k<'a, T: WithLifetime<'a>>(_x: T::Output) -> &'a isize {
7171
| ++
7272

73-
error: aborting due to 6 previous errors
73+
error[E0106]: missing lifetime specifier
74+
--> $DIR/lifetime-elision-return-type-requires-explicit-lifetime.rs:45:37
75+
|
76+
LL | fn l<'a>(_: &'a str, _: &'a str) -> &str { "" }
77+
| ------- ------- ^ expected named lifetime parameter
78+
|
79+
= help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments
80+
help: consider using the `'a` lifetime
81+
|
82+
LL | fn l<'a>(_: &'a str, _: &'a str) -> &'a str { "" }
83+
| ++
84+
85+
error: aborting due to 7 previous errors
7486

7587
For more information about this error, try `rustc --explain E0106`.

0 commit comments

Comments
 (0)