Skip to content

Commit cb1e7d9

Browse files
committed
Only ban duplication across parameters.
1 parent 47704bb commit cb1e7d9

File tree

3 files changed

+70
-31
lines changed

3 files changed

+70
-31
lines changed

compiler/rustc_resolve/src/late.rs

+66-31
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

@@ -1852,12 +1853,25 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
18521853
has_self: bool,
18531854
inputs: impl Iterator<Item = (Option<&'ast Pat>, &'ast Ty)>,
18541855
) -> Result<LifetimeRes, (Vec<MissingLifetime>, Vec<ElisionFnParameter>)> {
1855-
let outer_candidates =
1856-
replace(&mut self.lifetime_elision_candidates, Some(Default::default()));
1856+
enum Elision {
1857+
/// We have not found any candidate.
1858+
None,
1859+
/// We have a candidate bound to `self`.
1860+
Self_(LifetimeRes),
1861+
/// We have a candidate bound to a parameter.
1862+
Param(LifetimeRes),
1863+
/// We failed elision.
1864+
Err,
1865+
}
18571866

1858-
let mut elision_lifetime = None;
1859-
let mut lifetime_count = 0;
1867+
// Save elision state to reinstate it later.
1868+
let outer_candidates = self.lifetime_elision_candidates.take();
1869+
1870+
// Result of elision.
1871+
let mut elision_lifetime = Elision::None;
1872+
// Information for diagnostics.
18601873
let mut parameter_info = Vec::new();
1874+
let mut all_candidates = Vec::new();
18611875

18621876
let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())];
18631877
for (index, (pat, ty)) in inputs.enumerate() {
@@ -1867,61 +1881,82 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
18671881
this.resolve_pattern(pat, PatternSource::FnParam, &mut bindings);
18681882
}
18691883
});
1884+
1885+
// Record elision candidates only for this parameter.
1886+
debug_assert_matches!(self.lifetime_elision_candidates, None);
1887+
self.lifetime_elision_candidates = Some(Default::default());
18701888
self.visit_ty(ty);
1889+
let local_candidates = self.lifetime_elision_candidates.take();
18711890

1872-
if let Some(ref candidates) = self.lifetime_elision_candidates {
1873-
let new_count = candidates.len();
1874-
let local_count = new_count - lifetime_count;
1875-
if local_count != 0 {
1891+
if let Some(candidates) = local_candidates {
1892+
let distinct: FxHashSet<_> = candidates.iter().map(|(res, _)| *res).collect();
1893+
let lifetime_count = distinct.len();
1894+
if lifetime_count != 0 {
18761895
parameter_info.push(ElisionFnParameter {
18771896
index,
18781897
ident: if let Some(pat) = pat && let PatKind::Ident(_, ident, _) = pat.kind {
18791898
Some(ident)
18801899
} else {
18811900
None
18821901
},
1883-
lifetime_count: local_count,
1902+
lifetime_count,
18841903
span: ty.span,
18851904
});
1905+
all_candidates.extend(candidates.into_iter().filter_map(|(_, candidate)| {
1906+
match candidate {
1907+
LifetimeElisionCandidate::Ignore | LifetimeElisionCandidate::Named => {
1908+
None
1909+
}
1910+
LifetimeElisionCandidate::Missing(missing) => Some(missing),
1911+
}
1912+
}));
1913+
}
1914+
let mut distinct_iter = distinct.into_iter();
1915+
if let Some(res) = distinct_iter.next() {
1916+
match elision_lifetime {
1917+
// We are the first parameter to bind lifetimes.
1918+
Elision::None => {
1919+
if distinct_iter.next().is_none() {
1920+
// We have a single lifetime => success.
1921+
elision_lifetime = Elision::Param(res)
1922+
} else {
1923+
// We have have multiple lifetimes => error.
1924+
elision_lifetime = Elision::Err;
1925+
}
1926+
}
1927+
// We have 2 parameters that bind lifetimes => error.
1928+
Elision::Param(_) => elision_lifetime = Elision::Err,
1929+
// `self` elision takes precedence over everything else.
1930+
Elision::Self_(_) | Elision::Err => {}
1931+
}
18861932
}
1887-
lifetime_count = new_count;
18881933
}
18891934

18901935
// Handle `self` specially.
18911936
if index == 0 && has_self {
18921937
let self_lifetime = self.find_lifetime_for_self(ty);
18931938
if let Set1::One(lifetime) = self_lifetime {
1894-
elision_lifetime = Some(lifetime);
1895-
self.lifetime_elision_candidates = None;
1939+
// We found `self` elision.
1940+
elision_lifetime = Elision::Self_(lifetime);
18961941
} else {
1897-
self.lifetime_elision_candidates = Some(Default::default());
1898-
lifetime_count = 0;
1942+
// We do not have `self` elision: disregard the `Elision::Param` that we may
1943+
// have found.
1944+
elision_lifetime = Elision::None;
18991945
}
19001946
}
19011947
debug!("(resolving function / closure) recorded parameter");
19021948
}
19031949

1904-
let all_candidates = replace(&mut self.lifetime_elision_candidates, outer_candidates);
1905-
debug!(?all_candidates);
1950+
// Reinstate elision state.
1951+
debug_assert_matches!(self.lifetime_elision_candidates, None);
1952+
self.lifetime_elision_candidates = outer_candidates;
19061953

1907-
if let Some(res) = elision_lifetime {
1954+
if let Elision::Param(res) | Elision::Self_(res) = elision_lifetime {
19081955
return Ok(res);
19091956
}
19101957

1911-
// We do not have a `self` candidate, look at the full list.
1912-
let all_candidates = all_candidates.unwrap();
1913-
if let [(res, _)] = &all_candidates[..] {
1914-
Ok(*res)
1915-
} else {
1916-
let all_candidates = all_candidates
1917-
.into_iter()
1918-
.filter_map(|(_, candidate)| match candidate {
1919-
LifetimeElisionCandidate::Ignore | LifetimeElisionCandidate::Named => None,
1920-
LifetimeElisionCandidate::Missing(missing) => Some(missing),
1921-
})
1922-
.collect();
1923-
Err((all_candidates, parameter_info))
1924-
}
1958+
// We do not have a candidate.
1959+
Err((all_candidates, parameter_info))
19251960
}
19261961

19271962
/// List all the lifetimes that appear in the provided type.

compiler/rustc_resolve/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//! Type-relative name resolution (methods, fields, associated items) happens in `rustc_hir_analysis`.
88
99
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
10+
#![feature(assert_matches)]
1011
#![feature(box_patterns)]
1112
#![feature(drain_filter)]
1213
#![feature(if_let_guard)]

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

+3
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,7 @@ fn k<'a, T: WithLifetime<'a>>(_x: T::Output) -> &isize {
4545
fn l<'a>(_: &'a str, _: &'a str) -> &str { "" }
4646
//~^ ERROR missing lifetime specifier
4747

48+
// This is ok because both `'a` are for the same parameter.
49+
fn m<'a>(_: &'a Foo<'a>) -> &str { "" }
50+
4851
fn main() {}

0 commit comments

Comments
 (0)