Skip to content

Commit de01a29

Browse files
authored
Rollup merge of #68195 - estebank:impl-trait-2000, r=Centril
Account for common `impl Trait`/`dyn Trait` return type errors - When all return paths have the same type, suggest `impl Trait`. - When all return paths implement the expected `trait`, suggest `Box<dyn Trait>` and mention using an `enum`. - When multiple different types are returned and `impl Trait` is expected, extend the explanation. - When return type is `impl Trait` and the return paths do not implement `Trait`, point at the returned values. - Split `src/librustc/traits/error_reporting.rs` into multiple files to keep size under control. Fix #68110, cc #66523.
2 parents ecf42a3 + 029a9c6 commit de01a29

38 files changed

+4067
-3027
lines changed

src/librustc/traits/error_reporting.rs

-2,991
This file was deleted.

src/librustc/traits/error_reporting/mod.rs

+1,412
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
use super::{
2+
ObligationCauseCode, OnUnimplementedDirective, OnUnimplementedNote, PredicateObligation,
3+
};
4+
use crate::infer::InferCtxt;
5+
use crate::ty::subst::Subst;
6+
use crate::ty::{self, GenericParamDefKind};
7+
use rustc_hir as hir;
8+
use rustc_hir::def_id::DefId;
9+
use rustc_span::symbol::sym;
10+
11+
impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
12+
fn impl_similar_to(
13+
&self,
14+
trait_ref: ty::PolyTraitRef<'tcx>,
15+
obligation: &PredicateObligation<'tcx>,
16+
) -> Option<DefId> {
17+
let tcx = self.tcx;
18+
let param_env = obligation.param_env;
19+
let trait_ref = tcx.erase_late_bound_regions(&trait_ref);
20+
let trait_self_ty = trait_ref.self_ty();
21+
22+
let mut self_match_impls = vec![];
23+
let mut fuzzy_match_impls = vec![];
24+
25+
self.tcx.for_each_relevant_impl(trait_ref.def_id, trait_self_ty, |def_id| {
26+
let impl_substs = self.fresh_substs_for_item(obligation.cause.span, def_id);
27+
let impl_trait_ref = tcx.impl_trait_ref(def_id).unwrap().subst(tcx, impl_substs);
28+
29+
let impl_self_ty = impl_trait_ref.self_ty();
30+
31+
if let Ok(..) = self.can_eq(param_env, trait_self_ty, impl_self_ty) {
32+
self_match_impls.push(def_id);
33+
34+
if trait_ref
35+
.substs
36+
.types()
37+
.skip(1)
38+
.zip(impl_trait_ref.substs.types().skip(1))
39+
.all(|(u, v)| self.fuzzy_match_tys(u, v))
40+
{
41+
fuzzy_match_impls.push(def_id);
42+
}
43+
}
44+
});
45+
46+
let impl_def_id = if self_match_impls.len() == 1 {
47+
self_match_impls[0]
48+
} else if fuzzy_match_impls.len() == 1 {
49+
fuzzy_match_impls[0]
50+
} else {
51+
return None;
52+
};
53+
54+
tcx.has_attr(impl_def_id, sym::rustc_on_unimplemented).then_some(impl_def_id)
55+
}
56+
57+
/// Used to set on_unimplemented's `ItemContext`
58+
/// to be the enclosing (async) block/function/closure
59+
fn describe_enclosure(&self, hir_id: hir::HirId) -> Option<&'static str> {
60+
let hir = &self.tcx.hir();
61+
let node = hir.find(hir_id)?;
62+
if let hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, _, body_id), .. }) = &node {
63+
self.describe_generator(*body_id).or_else(|| {
64+
Some(if let hir::FnHeader { asyncness: hir::IsAsync::Async, .. } = sig.header {
65+
"an async function"
66+
} else {
67+
"a function"
68+
})
69+
})
70+
} else if let hir::Node::Expr(hir::Expr {
71+
kind: hir::ExprKind::Closure(_is_move, _, body_id, _, gen_movability),
72+
..
73+
}) = &node
74+
{
75+
self.describe_generator(*body_id).or_else(|| {
76+
Some(if gen_movability.is_some() { "an async closure" } else { "a closure" })
77+
})
78+
} else if let hir::Node::Expr(hir::Expr { .. }) = &node {
79+
let parent_hid = hir.get_parent_node(hir_id);
80+
if parent_hid != hir_id {
81+
return self.describe_enclosure(parent_hid);
82+
} else {
83+
None
84+
}
85+
} else {
86+
None
87+
}
88+
}
89+
90+
crate fn on_unimplemented_note(
91+
&self,
92+
trait_ref: ty::PolyTraitRef<'tcx>,
93+
obligation: &PredicateObligation<'tcx>,
94+
) -> OnUnimplementedNote {
95+
let def_id =
96+
self.impl_similar_to(trait_ref, obligation).unwrap_or_else(|| trait_ref.def_id());
97+
let trait_ref = *trait_ref.skip_binder();
98+
99+
let mut flags = vec![];
100+
flags.push((
101+
sym::item_context,
102+
self.describe_enclosure(obligation.cause.body_id).map(|s| s.to_owned()),
103+
));
104+
105+
match obligation.cause.code {
106+
ObligationCauseCode::BuiltinDerivedObligation(..)
107+
| ObligationCauseCode::ImplDerivedObligation(..) => {}
108+
_ => {
109+
// this is a "direct", user-specified, rather than derived,
110+
// obligation.
111+
flags.push((sym::direct, None));
112+
}
113+
}
114+
115+
if let ObligationCauseCode::ItemObligation(item) = obligation.cause.code {
116+
// FIXME: maybe also have some way of handling methods
117+
// from other traits? That would require name resolution,
118+
// which we might want to be some sort of hygienic.
119+
//
120+
// Currently I'm leaving it for what I need for `try`.
121+
if self.tcx.trait_of_item(item) == Some(trait_ref.def_id) {
122+
let method = self.tcx.item_name(item);
123+
flags.push((sym::from_method, None));
124+
flags.push((sym::from_method, Some(method.to_string())));
125+
}
126+
}
127+
if let Some((t, _)) = self.get_parent_trait_ref(&obligation.cause.code) {
128+
flags.push((sym::parent_trait, Some(t)));
129+
}
130+
131+
if let Some(k) = obligation.cause.span.desugaring_kind() {
132+
flags.push((sym::from_desugaring, None));
133+
flags.push((sym::from_desugaring, Some(format!("{:?}", k))));
134+
}
135+
let generics = self.tcx.generics_of(def_id);
136+
let self_ty = trait_ref.self_ty();
137+
// This is also included through the generics list as `Self`,
138+
// but the parser won't allow you to use it
139+
flags.push((sym::_Self, Some(self_ty.to_string())));
140+
if let Some(def) = self_ty.ty_adt_def() {
141+
// We also want to be able to select self's original
142+
// signature with no type arguments resolved
143+
flags.push((sym::_Self, Some(self.tcx.type_of(def.did).to_string())));
144+
}
145+
146+
for param in generics.params.iter() {
147+
let value = match param.kind {
148+
GenericParamDefKind::Type { .. } | GenericParamDefKind::Const => {
149+
trait_ref.substs[param.index as usize].to_string()
150+
}
151+
GenericParamDefKind::Lifetime => continue,
152+
};
153+
let name = param.name;
154+
flags.push((name, Some(value)));
155+
}
156+
157+
if let Some(true) = self_ty.ty_adt_def().map(|def| def.did.is_local()) {
158+
flags.push((sym::crate_local, None));
159+
}
160+
161+
// Allow targeting all integers using `{integral}`, even if the exact type was resolved
162+
if self_ty.is_integral() {
163+
flags.push((sym::_Self, Some("{integral}".to_owned())));
164+
}
165+
166+
if let ty::Array(aty, len) = self_ty.kind {
167+
flags.push((sym::_Self, Some("[]".to_owned())));
168+
flags.push((sym::_Self, Some(format!("[{}]", aty))));
169+
if let Some(def) = aty.ty_adt_def() {
170+
// We also want to be able to select the array's type's original
171+
// signature with no type arguments resolved
172+
flags.push((
173+
sym::_Self,
174+
Some(format!("[{}]", self.tcx.type_of(def.did).to_string())),
175+
));
176+
let tcx = self.tcx;
177+
if let Some(len) = len.try_eval_usize(tcx, ty::ParamEnv::empty()) {
178+
flags.push((
179+
sym::_Self,
180+
Some(format!("[{}; {}]", self.tcx.type_of(def.did).to_string(), len)),
181+
));
182+
} else {
183+
flags.push((
184+
sym::_Self,
185+
Some(format!("[{}; _]", self.tcx.type_of(def.did).to_string())),
186+
));
187+
}
188+
}
189+
}
190+
191+
if let Ok(Some(command)) =
192+
OnUnimplementedDirective::of_item(self.tcx, trait_ref.def_id, def_id)
193+
{
194+
command.evaluate(self.tcx, trait_ref, &flags[..])
195+
} else {
196+
OnUnimplementedNote::default()
197+
}
198+
}
199+
}

0 commit comments

Comments
 (0)