Skip to content

Commit 88cb279

Browse files
committed
support duplicate where-bounds
1 parent aef4c7a commit 88cb279

File tree

1 file changed

+58
-47
lines changed
  • compiler/rustc_trait_selection/src/traits/select

1 file changed

+58
-47
lines changed

compiler/rustc_trait_selection/src/traits/select/mod.rs

+58-47
Original file line numberDiff line numberDiff line change
@@ -1779,53 +1779,60 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
17791779
return Some(BuiltinCandidate { has_nested: false });
17801780
}
17811781

1782-
// The next highest priority is for non-global where-bounds. However, there are
1783-
// two additional rules here:
1784-
// - if there are also global where-bound which may apply, we bail with ambiguity
1785-
// instead of prefering the non-global where-bound. Note that if there are
1786-
// only global where-bounds, they get ignored
1787-
// - if there are two where-bounds which only differ in their bound vars, but are
1788-
// otherwise equal, we arbitrarily prefer one of them
1782+
// Before we consider where-bounds, we have to deduplicate them here and also
1783+
// dropwhere-bounds in case the same where-bound exists without bound vars.
1784+
// This is necessary as elaborating super-trait bounds may result in duplicates.
1785+
'search_victim: loop {
1786+
for (i, this) in candidates.iter().enumerate() {
1787+
let ParamCandidate(this) = this.candidate else { continue };
1788+
for (j, other) in candidates.iter().enumerate() {
1789+
if i == j {
1790+
continue;
1791+
}
1792+
1793+
let ParamCandidate(other) = other.candidate else { continue };
1794+
if this == other {
1795+
candidates.remove(j);
1796+
continue 'search_victim;
1797+
}
1798+
1799+
if this.skip_binder().trait_ref == other.skip_binder().trait_ref
1800+
&& this.skip_binder().polarity == other.skip_binder().polarity
1801+
&& !this.skip_binder().trait_ref.has_escaping_bound_vars()
1802+
{
1803+
candidates.remove(j);
1804+
continue 'search_victim;
1805+
}
1806+
}
1807+
}
1808+
1809+
break;
1810+
}
1811+
1812+
// The next highest priority is for non-global where-bounds. However, while we don't
1813+
// prefer global where-clauses here, we do bail with ambiguity when encountering both
1814+
// a global and a non-global where-clause.
17891815
//
1790-
// This is fairly messy but necessary for backwards compatability, see #50825 for why
1791-
// we need to handle global where-bounds like this.
1816+
// Our handling of where-bounds is generally fairly messy but necessary for backwards
1817+
// compatability, see #50825 for why we need to handle global where-bounds like this.
1818+
let is_global = |c: ty::PolyTraitPredicate<'tcx>| c.is_global() && !c.has_bound_vars();
17921819
let param_candidates = candidates
17931820
.iter()
17941821
.filter_map(|c| if let ParamCandidate(p) = c.candidate { Some(p) } else { None });
1795-
let is_global = |c: ty::PolyTraitPredicate<'tcx>| c.is_global() && !c.has_bound_vars();
1796-
let mut has_non_global = false;
1822+
let mut has_global_bounds = false;
17971823
let mut param_candidate = None;
17981824
for c in param_candidates {
1799-
has_non_global |= !is_global(c);
1800-
let has_fewer_bound_vars =
1801-
|this: ty::PolyTraitPredicate<'tcx>, other: ty::PolyTraitPredicate<'tcx>| {
1802-
this.skip_binder().trait_ref == other.skip_binder().trait_ref
1803-
&& this.skip_binder().polarity == other.skip_binder().polarity
1804-
&& !this.skip_binder().trait_ref.has_escaping_bound_vars()
1805-
};
1806-
if let Some(prev) = param_candidate.replace(c) {
1807-
if has_fewer_bound_vars(c, prev) {
1808-
// Ok, prefer `c` over the previous entry
1809-
} else if has_fewer_bound_vars(prev, c) {
1810-
// Ok, keep `prev` instead of the new entry
1811-
param_candidate = Some(prev);
1812-
} else {
1813-
// Ambiguity, two potentially different where-clauses
1814-
return None;
1815-
}
1825+
if is_global(c) {
1826+
has_global_bounds = true;
1827+
} else if param_candidate.replace(c).is_some() {
1828+
// Ambiguity, two potentially different where-clauses
1829+
return None;
18161830
}
18171831
}
18181832
if let Some(predicate) = param_candidate {
1819-
if is_global(predicate) {
1820-
// If we only have global where-bounds, we prefer
1821-
// impls over them, otherwise we bail with ambiguity.
1822-
//
1823-
// This is only reachable if there's one higher-ranked
1824-
// and one non-higher ranked version of a global
1825-
// where-clause.
1826-
if has_non_global {
1827-
return None;
1828-
}
1833+
// Ambiguity, a global and a non-global where-bound.
1834+
if has_global_bounds {
1835+
return None;
18291836
} else {
18301837
return Some(ParamCandidate(predicate));
18311838
}
@@ -1912,15 +1919,19 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
19121919
}
19131920
}
19141921

1915-
// Also try ignoring all global where-bounds and check whether we end
1916-
// with a unique candidate in this case.
1917-
let mut not_a_global_where_bound = candidates
1918-
.into_iter()
1919-
.filter(|c| !matches!(c.candidate, ParamCandidate(p) if is_global(p)));
1920-
not_a_global_where_bound
1921-
.next()
1922-
.map(|c| c.candidate)
1923-
.filter(|_| not_a_global_where_bound.next().is_none())
1922+
if candidates.len() == 1 {
1923+
Some(candidates.pop().unwrap().candidate)
1924+
} else {
1925+
// Also try ignoring all global where-bounds and check whether we end
1926+
// with a unique candidate in this case.
1927+
let mut not_a_global_where_bound = candidates
1928+
.into_iter()
1929+
.filter(|c| !matches!(c.candidate, ParamCandidate(p) if is_global(p)));
1930+
not_a_global_where_bound
1931+
.next()
1932+
.map(|c| c.candidate)
1933+
.filter(|_| not_a_global_where_bound.next().is_none())
1934+
}
19241935
}
19251936

19261937
fn prefer_lhs_over_victim(

0 commit comments

Comments
 (0)