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