Skip to content

Commit 8904b64

Browse files
committed
On trait bound mismatch, detect multiple crate versions in dep tree
When encountering an E0277, if the type and the trait both come from a crate with the same name but different crate number, we explain that there are multiple crate versions in the dependency tree. If there's a type that fulfills the bound, and it has the same name as the passed in type and has the same crate name, we explain that the same type in two different versions of the same crate *are different*. ``` error[E0277]: the trait bound `Type: dependency::Trait` is not satisfied --> src/main.rs:4:18 | 4 | do_something(Type); | ------------ ^^^^ the trait `dependency::Trait` is not implemented for `Type` | | | required by a bound introduced by this call | help: you have multiple different versions of crate `dependency` in your dependency graph --> src/main.rs:1:5 | 1 | use bar::do_something; | ^^^ one version of crate `dependency` is used here, as a dependency of crate `bar` 2 | use dependency::Type; | ^^^^^^^^^^ one version of crate `dependency` is used here, as a direct dependency of the current crate note: two types coming from two different versions of the same crate are different types even if they look the same --> /home/gh-estebank/crate_versions/baz-2/src/lib.rs:1:1 | 1 | pub struct Type; | ^^^^^^^^^^^^^^^ this type doesn't implement the required trait | ::: /home/gh-estebank/crate_versions/baz/src/lib.rs:1:1 | 1 | pub struct Type; | ^^^^^^^^^^^^^^^ this type implements the required trait 2 | pub trait Trait {} | --------------- this is the required trait note: required by a bound in `bar::do_something` --> /home/gh-estebank/crate_versions/baz/src/lib.rs:4:24 | 4 | pub fn do_something<X: Trait>(_: X) {} | ^^^^^ required by this bound in `do_something` ``` Address #22750.
1 parent 8982c7f commit 8904b64

File tree

1 file changed

+121
-33
lines changed

1 file changed

+121
-33
lines changed

compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs

Lines changed: 121 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1940,9 +1940,127 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
19401940
other: bool,
19411941
param_env: ty::ParamEnv<'tcx>,
19421942
) -> bool {
1943-
// If we have a single implementation, try to unify it with the trait ref
1944-
// that failed. This should uncover a better hint for what *is* implemented.
1943+
let alternative_candidates = |def_id: DefId| {
1944+
let mut impl_candidates: Vec<_> = self
1945+
.tcx
1946+
.all_impls(def_id)
1947+
// Ignore automatically derived impls and `!Trait` impls.
1948+
.filter_map(|def_id| self.tcx.impl_trait_header(def_id))
1949+
.filter_map(|header| {
1950+
(header.polarity != ty::ImplPolarity::Negative
1951+
|| self.tcx.is_automatically_derived(def_id))
1952+
.then(|| header.trait_ref.instantiate_identity())
1953+
})
1954+
.filter(|trait_ref| {
1955+
let self_ty = trait_ref.self_ty();
1956+
// Avoid mentioning type parameters.
1957+
if let ty::Param(_) = self_ty.kind() {
1958+
false
1959+
}
1960+
// Avoid mentioning types that are private to another crate
1961+
else if let ty::Adt(def, _) = self_ty.peel_refs().kind() {
1962+
// FIXME(compiler-errors): This could be generalized, both to
1963+
// be more granular, and probably look past other `#[fundamental]`
1964+
// types, too.
1965+
self.tcx.visibility(def.did()).is_accessible_from(body_def_id, self.tcx)
1966+
} else {
1967+
true
1968+
}
1969+
})
1970+
.collect();
1971+
1972+
impl_candidates.sort_by_key(|tr| tr.to_string());
1973+
impl_candidates.dedup();
1974+
impl_candidates
1975+
};
1976+
1977+
// We'll check for the case where the reason for the mismatch is that the trait comes from
1978+
// one crate version and the type comes from another crate version, even though they both
1979+
// are from the same crate.
1980+
let trait_def_id = trait_ref.def_id();
1981+
if let ty::Adt(def, _) = trait_ref.self_ty().skip_binder().peel_refs().kind()
1982+
&& let found_type = def.did()
1983+
&& trait_def_id.krate != found_type.krate
1984+
&& self.tcx.crate_name(trait_def_id.krate) == self.tcx.crate_name(found_type.krate)
1985+
{
1986+
let name = self.tcx.crate_name(trait_def_id.krate);
1987+
let spans: Vec<_> = [trait_def_id, found_type]
1988+
.into_iter()
1989+
.filter_map(|def_id| self.tcx.extern_crate(def_id))
1990+
.map(|data| {
1991+
let dependency = if data.dependency_of == LOCAL_CRATE {
1992+
"direct dependency of the current crate".to_string()
1993+
} else {
1994+
let dep = self.tcx.crate_name(data.dependency_of);
1995+
format!("dependency of crate `{dep}`")
1996+
};
1997+
(
1998+
data.span,
1999+
format!("one version of crate `{name}` is used here, as a {dependency}"),
2000+
)
2001+
})
2002+
.collect();
2003+
let mut span: MultiSpan = spans.iter().map(|(sp, _)| *sp).collect::<Vec<Span>>().into();
2004+
for (sp, label) in spans.into_iter() {
2005+
span.push_span_label(sp, label);
2006+
}
2007+
err.highlighted_span_help(
2008+
span,
2009+
vec![
2010+
StringPart::normal("you have ".to_string()),
2011+
StringPart::highlighted("multiple different versions".to_string()),
2012+
StringPart::normal(" of crate `".to_string()),
2013+
StringPart::highlighted(format!("{name}")),
2014+
StringPart::normal("` in your dependency graph".to_string()),
2015+
],
2016+
);
2017+
let candidates = if impl_candidates.is_empty() {
2018+
alternative_candidates(trait_def_id)
2019+
} else {
2020+
impl_candidates.into_iter().map(|cand| cand.trait_ref).collect()
2021+
};
2022+
if let Some((sp_candidate, sp_found)) = candidates.iter().find_map(|trait_ref| {
2023+
if let ty::Adt(def, _) = trait_ref.self_ty().peel_refs().kind()
2024+
&& let candidate_def_id = def.did()
2025+
&& let Some(name) = self.tcx.opt_item_name(candidate_def_id)
2026+
&& let Some(found) = self.tcx.opt_item_name(found_type)
2027+
&& name == found
2028+
&& candidate_def_id.krate != found_type.krate
2029+
&& self.tcx.crate_name(candidate_def_id.krate)
2030+
== self.tcx.crate_name(found_type.krate)
2031+
{
2032+
// A candidate was found of an item with the same name, from two separate
2033+
// versions of the same crate, let's clarify.
2034+
Some((self.tcx.def_span(candidate_def_id), self.tcx.def_span(found_type)))
2035+
} else {
2036+
None
2037+
}
2038+
}) {
2039+
let mut span: MultiSpan = vec![sp_candidate, sp_found].into();
2040+
span.push_span_label(self.tcx.def_span(trait_def_id), "this is the required trait");
2041+
span.push_span_label(sp_candidate, "this type implements the required trait");
2042+
span.push_span_label(
2043+
sp_found,
2044+
"this type doesn't implement the required trait",
2045+
);
2046+
err.highlighted_span_note(
2047+
span,
2048+
vec![
2049+
StringPart::normal(
2050+
"two types coming from two different versions of the same crate are \
2051+
different types "
2052+
.to_string(),
2053+
),
2054+
StringPart::highlighted("even if they look the same".to_string()),
2055+
],
2056+
);
2057+
}
2058+
return true;
2059+
}
2060+
19452061
if let [single] = &impl_candidates {
2062+
// If we have a single implementation, try to unify it with the trait ref
2063+
// that failed. This should uncover a better hint for what *is* implemented.
19462064
if self.probe(|_| {
19472065
let ocx = ObligationCtxt::new(self);
19482066

@@ -2101,37 +2219,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
21012219
// Mentioning implementers of `Copy`, `Debug` and friends is not useful.
21022220
return false;
21032221
}
2104-
let mut impl_candidates: Vec<_> = self
2105-
.tcx
2106-
.all_impls(def_id)
2107-
// Ignore automatically derived impls and `!Trait` impls.
2108-
.filter_map(|def_id| self.tcx.impl_trait_header(def_id))
2109-
.filter_map(|header| {
2110-
(header.polarity != ty::ImplPolarity::Negative
2111-
|| self.tcx.is_automatically_derived(def_id))
2112-
.then(|| header.trait_ref.instantiate_identity())
2113-
})
2114-
.filter(|trait_ref| {
2115-
let self_ty = trait_ref.self_ty();
2116-
// Avoid mentioning type parameters.
2117-
if let ty::Param(_) = self_ty.kind() {
2118-
false
2119-
}
2120-
// Avoid mentioning types that are private to another crate
2121-
else if let ty::Adt(def, _) = self_ty.peel_refs().kind() {
2122-
// FIXME(compiler-errors): This could be generalized, both to
2123-
// be more granular, and probably look past other `#[fundamental]`
2124-
// types, too.
2125-
self.tcx.visibility(def.did()).is_accessible_from(body_def_id, self.tcx)
2126-
} else {
2127-
true
2128-
}
2129-
})
2130-
.collect();
2131-
2132-
impl_candidates.sort_by_key(|tr| tr.to_string());
2133-
impl_candidates.dedup();
2134-
return report(impl_candidates, err);
2222+
return report(alternative_candidates(def_id), err);
21352223
}
21362224

21372225
// Sort impl candidates so that ordering is consistent for UI tests.

0 commit comments

Comments
 (0)