]> git.proxmox.com Git - rustc.git/blob - compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
New upstream version 1.61.0+dfsg1
[rustc.git] / compiler / rustc_trait_selection / src / traits / error_reporting / on_unimplemented.rs
1 use super::{
2 ObligationCauseCode, OnUnimplementedDirective, OnUnimplementedNote, PredicateObligation,
3 };
4 use crate::infer::InferCtxt;
5 use rustc_hir as hir;
6 use rustc_hir::def_id::DefId;
7 use rustc_middle::ty::subst::{Subst, SubstsRef};
8 use rustc_middle::ty::{self, GenericParamDefKind};
9 use rustc_span::symbol::sym;
10 use std::iter;
11
12 use super::InferCtxtPrivExt;
13
14 pub trait InferCtxtExt<'tcx> {
15 /*private*/
16 fn impl_similar_to(
17 &self,
18 trait_ref: ty::PolyTraitRef<'tcx>,
19 obligation: &PredicateObligation<'tcx>,
20 ) -> Option<(DefId, SubstsRef<'tcx>)>;
21
22 /*private*/
23 fn describe_enclosure(&self, hir_id: hir::HirId) -> Option<&'static str>;
24
25 fn on_unimplemented_note(
26 &self,
27 trait_ref: ty::PolyTraitRef<'tcx>,
28 obligation: &PredicateObligation<'tcx>,
29 ) -> OnUnimplementedNote;
30 }
31
32 impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
33 fn impl_similar_to(
34 &self,
35 trait_ref: ty::PolyTraitRef<'tcx>,
36 obligation: &PredicateObligation<'tcx>,
37 ) -> Option<(DefId, SubstsRef<'tcx>)> {
38 let tcx = self.tcx;
39 let param_env = obligation.param_env;
40 let trait_ref = tcx.erase_late_bound_regions(trait_ref);
41 let trait_self_ty = trait_ref.self_ty();
42
43 let mut self_match_impls = vec![];
44 let mut fuzzy_match_impls = vec![];
45
46 self.tcx.for_each_relevant_impl(trait_ref.def_id, trait_self_ty, |def_id| {
47 let impl_substs = self.fresh_substs_for_item(obligation.cause.span, def_id);
48 let impl_trait_ref = tcx.impl_trait_ref(def_id).unwrap().subst(tcx, impl_substs);
49
50 let impl_self_ty = impl_trait_ref.self_ty();
51
52 if let Ok(..) = self.can_eq(param_env, trait_self_ty, impl_self_ty) {
53 self_match_impls.push((def_id, impl_substs));
54
55 if iter::zip(
56 trait_ref.substs.types().skip(1),
57 impl_trait_ref.substs.types().skip(1),
58 )
59 .all(|(u, v)| self.fuzzy_match_tys(u, v, false).is_some())
60 {
61 fuzzy_match_impls.push((def_id, impl_substs));
62 }
63 }
64 });
65
66 let impl_def_id_and_substs = if self_match_impls.len() == 1 {
67 self_match_impls[0]
68 } else if fuzzy_match_impls.len() == 1 {
69 fuzzy_match_impls[0]
70 } else {
71 return None;
72 };
73
74 tcx.has_attr(impl_def_id_and_substs.0, sym::rustc_on_unimplemented)
75 .then_some(impl_def_id_and_substs)
76 }
77
78 /// Used to set on_unimplemented's `ItemContext`
79 /// to be the enclosing (async) block/function/closure
80 fn describe_enclosure(&self, hir_id: hir::HirId) -> Option<&'static str> {
81 let hir = self.tcx.hir();
82 let node = hir.find(hir_id)?;
83 match &node {
84 hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, _, body_id), .. }) => {
85 self.describe_generator(*body_id).or_else(|| {
86 Some(match sig.header {
87 hir::FnHeader { asyncness: hir::IsAsync::Async, .. } => "an async function",
88 _ => "a function",
89 })
90 })
91 }
92 hir::Node::TraitItem(hir::TraitItem {
93 kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body_id)),
94 ..
95 }) => self.describe_generator(*body_id).or_else(|| Some("a trait method")),
96 hir::Node::ImplItem(hir::ImplItem {
97 kind: hir::ImplItemKind::Fn(sig, body_id),
98 ..
99 }) => self.describe_generator(*body_id).or_else(|| {
100 Some(match sig.header {
101 hir::FnHeader { asyncness: hir::IsAsync::Async, .. } => "an async method",
102 _ => "a method",
103 })
104 }),
105 hir::Node::Expr(hir::Expr {
106 kind: hir::ExprKind::Closure(_is_move, _, body_id, _, gen_movability),
107 ..
108 }) => self.describe_generator(*body_id).or_else(|| {
109 Some(if gen_movability.is_some() { "an async closure" } else { "a closure" })
110 }),
111 hir::Node::Expr(hir::Expr { .. }) => {
112 let parent_hid = hir.get_parent_node(hir_id);
113 if parent_hid != hir_id { self.describe_enclosure(parent_hid) } else { None }
114 }
115 _ => None,
116 }
117 }
118
119 fn on_unimplemented_note(
120 &self,
121 trait_ref: ty::PolyTraitRef<'tcx>,
122 obligation: &PredicateObligation<'tcx>,
123 ) -> OnUnimplementedNote {
124 let (def_id, substs) = self
125 .impl_similar_to(trait_ref, obligation)
126 .unwrap_or_else(|| (trait_ref.def_id(), trait_ref.skip_binder().substs));
127 let trait_ref = trait_ref.skip_binder();
128
129 let mut flags = vec![(
130 sym::ItemContext,
131 self.describe_enclosure(obligation.cause.body_id).map(|s| s.to_owned()),
132 )];
133
134 match obligation.cause.code() {
135 ObligationCauseCode::BuiltinDerivedObligation(..)
136 | ObligationCauseCode::ImplDerivedObligation(..)
137 | ObligationCauseCode::DerivedObligation(..) => {}
138 _ => {
139 // this is a "direct", user-specified, rather than derived,
140 // obligation.
141 flags.push((sym::direct, None));
142 }
143 }
144
145 if let ObligationCauseCode::ItemObligation(item)
146 | ObligationCauseCode::BindingObligation(item, _) = *obligation.cause.code()
147 {
148 // FIXME: maybe also have some way of handling methods
149 // from other traits? That would require name resolution,
150 // which we might want to be some sort of hygienic.
151 //
152 // Currently I'm leaving it for what I need for `try`.
153 if self.tcx.trait_of_item(item) == Some(trait_ref.def_id) {
154 let method = self.tcx.item_name(item);
155 flags.push((sym::from_method, None));
156 flags.push((sym::from_method, Some(method.to_string())));
157 }
158 }
159
160 if let Some(k) = obligation.cause.span.desugaring_kind() {
161 flags.push((sym::from_desugaring, None));
162 flags.push((sym::from_desugaring, Some(format!("{:?}", k))));
163 }
164
165 // Add all types without trimmed paths.
166 ty::print::with_no_trimmed_paths!({
167 let generics = self.tcx.generics_of(def_id);
168 let self_ty = trait_ref.self_ty();
169 // This is also included through the generics list as `Self`,
170 // but the parser won't allow you to use it
171 flags.push((sym::_Self, Some(self_ty.to_string())));
172 if let Some(def) = self_ty.ty_adt_def() {
173 // We also want to be able to select self's original
174 // signature with no type arguments resolved
175 flags.push((sym::_Self, Some(self.tcx.type_of(def.did()).to_string())));
176 }
177
178 for param in generics.params.iter() {
179 let value = match param.kind {
180 GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => {
181 substs[param.index as usize].to_string()
182 }
183 GenericParamDefKind::Lifetime => continue,
184 };
185 let name = param.name;
186 flags.push((name, Some(value)));
187
188 if let GenericParamDefKind::Type { .. } = param.kind {
189 let param_ty = substs[param.index as usize].expect_ty();
190 if let Some(def) = param_ty.ty_adt_def() {
191 // We also want to be able to select the parameter's
192 // original signature with no type arguments resolved
193 flags.push((name, Some(self.tcx.type_of(def.did()).to_string())));
194 }
195 }
196 }
197
198 if let Some(true) = self_ty.ty_adt_def().map(|def| def.did().is_local()) {
199 flags.push((sym::crate_local, None));
200 }
201
202 // Allow targeting all integers using `{integral}`, even if the exact type was resolved
203 if self_ty.is_integral() {
204 flags.push((sym::_Self, Some("{integral}".to_owned())));
205 }
206
207 if self_ty.is_array_slice() {
208 flags.push((sym::_Self, Some("&[]".to_owned())));
209 }
210
211 if let ty::Array(aty, len) = self_ty.kind() {
212 flags.push((sym::_Self, Some("[]".to_owned())));
213 flags.push((sym::_Self, Some(format!("[{}]", aty))));
214 if let Some(def) = aty.ty_adt_def() {
215 // We also want to be able to select the array's type's original
216 // signature with no type arguments resolved
217 let type_string = self.tcx.type_of(def.did()).to_string();
218 flags.push((sym::_Self, Some(format!("[{}]", type_string))));
219
220 let len =
221 len.val().try_to_value().and_then(|v| v.try_to_machine_usize(self.tcx));
222 let string = match len {
223 Some(n) => format!("[{}; {}]", type_string, n),
224 None => format!("[{}; _]", type_string),
225 };
226 flags.push((sym::_Self, Some(string)));
227 }
228 }
229 if let ty::Dynamic(traits, _) = self_ty.kind() {
230 for t in traits.iter() {
231 if let ty::ExistentialPredicate::Trait(trait_ref) = t.skip_binder() {
232 flags.push((sym::_Self, Some(self.tcx.def_path_str(trait_ref.def_id))))
233 }
234 }
235 }
236 });
237
238 if let Ok(Some(command)) = OnUnimplementedDirective::of_item(self.tcx, def_id) {
239 command.evaluate(self.tcx, trait_ref, &flags)
240 } else {
241 OnUnimplementedNote::default()
242 }
243 }
244 }