]>
Commit | Line | Data |
---|---|---|
dfeec247 XL |
1 | use super::{ |
2 | ObligationCauseCode, OnUnimplementedDirective, OnUnimplementedNote, PredicateObligation, | |
3 | }; | |
2b03887a | 4 | use crate::infer::error_reporting::TypeErrCtxt; |
dfeec247 XL |
5 | use rustc_hir as hir; |
6 | use rustc_hir::def_id::DefId; | |
2b03887a | 7 | use rustc_middle::ty::SubstsRef; |
ba9703b0 | 8 | use rustc_middle::ty::{self, GenericParamDefKind}; |
dfeec247 | 9 | use rustc_span::symbol::sym; |
cdc7bbd5 | 10 | use std::iter; |
dfeec247 | 11 | |
ba9703b0 XL |
12 | use super::InferCtxtPrivExt; |
13 | ||
2b03887a | 14 | pub trait TypeErrCtxtExt<'tcx> { |
ba9703b0 XL |
15 | /*private*/ |
16 | fn impl_similar_to( | |
17 | &self, | |
18 | trait_ref: ty::PolyTraitRef<'tcx>, | |
19 | obligation: &PredicateObligation<'tcx>, | |
5e7ed085 | 20 | ) -> Option<(DefId, SubstsRef<'tcx>)>; |
ba9703b0 XL |
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 | ||
2b03887a | 32 | impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { |
dfeec247 XL |
33 | fn impl_similar_to( |
34 | &self, | |
35 | trait_ref: ty::PolyTraitRef<'tcx>, | |
36 | obligation: &PredicateObligation<'tcx>, | |
5e7ed085 | 37 | ) -> Option<(DefId, SubstsRef<'tcx>)> { |
dfeec247 XL |
38 | let tcx = self.tcx; |
39 | let param_env = obligation.param_env; | |
fc512014 | 40 | let trait_ref = tcx.erase_late_bound_regions(trait_ref); |
dfeec247 XL |
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); | |
04454e1e | 48 | let impl_trait_ref = tcx.bound_impl_trait_ref(def_id).unwrap().subst(tcx, impl_substs); |
dfeec247 XL |
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) { | |
5e7ed085 | 53 | self_match_impls.push((def_id, impl_substs)); |
dfeec247 | 54 | |
cdc7bbd5 XL |
55 | if iter::zip( |
56 | trait_ref.substs.types().skip(1), | |
57 | impl_trait_ref.substs.types().skip(1), | |
58 | ) | |
5099ac24 | 59 | .all(|(u, v)| self.fuzzy_match_tys(u, v, false).is_some()) |
dfeec247 | 60 | { |
5e7ed085 | 61 | fuzzy_match_impls.push((def_id, impl_substs)); |
dfeec247 XL |
62 | } |
63 | } | |
64 | }); | |
65 | ||
5e7ed085 | 66 | let impl_def_id_and_substs = if self_match_impls.len() == 1 { |
dfeec247 XL |
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 | ||
5e7ed085 FG |
74 | tcx.has_attr(impl_def_id_and_substs.0, sym::rustc_on_unimplemented) |
75 | .then_some(impl_def_id_and_substs) | |
dfeec247 XL |
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> { | |
5099ac24 | 81 | let hir = self.tcx.hir(); |
dfeec247 XL |
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(|| { | |
ba9703b0 XL |
86 | Some(match sig.header { |
87 | hir::FnHeader { asyncness: hir::IsAsync::Async, .. } => "an async function", | |
88 | _ => "a function", | |
dfeec247 XL |
89 | }) |
90 | }) | |
91 | } | |
92 | hir::Node::TraitItem(hir::TraitItem { | |
ba9703b0 | 93 | kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body_id)), |
dfeec247 XL |
94 | .. |
95 | }) => self.describe_generator(*body_id).or_else(|| Some("a trait method")), | |
96 | hir::Node::ImplItem(hir::ImplItem { | |
ba9703b0 | 97 | kind: hir::ImplItemKind::Fn(sig, body_id), |
dfeec247 XL |
98 | .. |
99 | }) => self.describe_generator(*body_id).or_else(|| { | |
ba9703b0 XL |
100 | Some(match sig.header { |
101 | hir::FnHeader { asyncness: hir::IsAsync::Async, .. } => "an async method", | |
102 | _ => "a method", | |
dfeec247 XL |
103 | }) |
104 | }), | |
105 | hir::Node::Expr(hir::Expr { | |
064997fb | 106 | kind: hir::ExprKind::Closure(hir::Closure { body, movability, .. }), |
dfeec247 | 107 | .. |
923072b8 FG |
108 | }) => self.describe_generator(*body).or_else(|| { |
109 | Some(if movability.is_some() { "an async closure" } else { "a closure" }) | |
dfeec247 XL |
110 | }), |
111 | hir::Node::Expr(hir::Expr { .. }) => { | |
112 | let parent_hid = hir.get_parent_node(hir_id); | |
ba9703b0 | 113 | if parent_hid != hir_id { self.describe_enclosure(parent_hid) } else { None } |
dfeec247 XL |
114 | } |
115 | _ => None, | |
116 | } | |
117 | } | |
118 | ||
ba9703b0 | 119 | fn on_unimplemented_note( |
dfeec247 XL |
120 | &self, |
121 | trait_ref: ty::PolyTraitRef<'tcx>, | |
122 | obligation: &PredicateObligation<'tcx>, | |
123 | ) -> OnUnimplementedNote { | |
5e7ed085 FG |
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)); | |
f035d41b | 127 | let trait_ref = trait_ref.skip_binder(); |
dfeec247 | 128 | |
94222f64 | 129 | let mut flags = vec![( |
3dfed10e | 130 | sym::ItemContext, |
dfeec247 | 131 | self.describe_enclosure(obligation.cause.body_id).map(|s| s.to_owned()), |
94222f64 | 132 | )]; |
dfeec247 | 133 | |
a2a8927a | 134 | match obligation.cause.code() { |
dfeec247 | 135 | ObligationCauseCode::BuiltinDerivedObligation(..) |
ba9703b0 XL |
136 | | ObligationCauseCode::ImplDerivedObligation(..) |
137 | | ObligationCauseCode::DerivedObligation(..) => {} | |
dfeec247 XL |
138 | _ => { |
139 | // this is a "direct", user-specified, rather than derived, | |
140 | // obligation. | |
141 | flags.push((sym::direct, None)); | |
142 | } | |
143 | } | |
144 | ||
ba9703b0 | 145 | if let ObligationCauseCode::ItemObligation(item) |
f2b60f7d FG |
146 | | ObligationCauseCode::BindingObligation(item, _) |
147 | | ObligationCauseCode::ExprItemObligation(item, ..) | |
148 | | ObligationCauseCode::ExprBindingObligation(item, ..) = *obligation.cause.code() | |
ba9703b0 | 149 | { |
dfeec247 XL |
150 | // FIXME: maybe also have some way of handling methods |
151 | // from other traits? That would require name resolution, | |
152 | // which we might want to be some sort of hygienic. | |
153 | // | |
154 | // Currently I'm leaving it for what I need for `try`. | |
155 | if self.tcx.trait_of_item(item) == Some(trait_ref.def_id) { | |
156 | let method = self.tcx.item_name(item); | |
157 | flags.push((sym::from_method, None)); | |
158 | flags.push((sym::from_method, Some(method.to_string()))); | |
159 | } | |
160 | } | |
dfeec247 XL |
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 | } | |
dfeec247 | 166 | |
2b03887a FG |
167 | if let ObligationCauseCode::MainFunctionType = obligation.cause.code() { |
168 | flags.push((sym::cause, Some("MainFunctionType".to_string()))); | |
169 | } | |
170 | ||
cdc7bbd5 | 171 | // Add all types without trimmed paths. |
5e7ed085 | 172 | ty::print::with_no_trimmed_paths!({ |
cdc7bbd5 XL |
173 | let generics = self.tcx.generics_of(def_id); |
174 | let self_ty = trait_ref.self_ty(); | |
175 | // This is also included through the generics list as `Self`, | |
176 | // but the parser won't allow you to use it | |
177 | flags.push((sym::_Self, Some(self_ty.to_string()))); | |
178 | if let Some(def) = self_ty.ty_adt_def() { | |
179 | // We also want to be able to select self's original | |
180 | // signature with no type arguments resolved | |
5e7ed085 | 181 | flags.push((sym::_Self, Some(self.tcx.type_of(def.did()).to_string()))); |
cdc7bbd5 | 182 | } |
dfeec247 | 183 | |
cdc7bbd5 XL |
184 | for param in generics.params.iter() { |
185 | let value = match param.kind { | |
186 | GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => { | |
5e7ed085 | 187 | substs[param.index as usize].to_string() |
cdc7bbd5 XL |
188 | } |
189 | GenericParamDefKind::Lifetime => continue, | |
190 | }; | |
191 | let name = param.name; | |
192 | flags.push((name, Some(value))); | |
17df50a5 XL |
193 | |
194 | if let GenericParamDefKind::Type { .. } = param.kind { | |
5e7ed085 | 195 | let param_ty = substs[param.index as usize].expect_ty(); |
17df50a5 XL |
196 | if let Some(def) = param_ty.ty_adt_def() { |
197 | // We also want to be able to select the parameter's | |
198 | // original signature with no type arguments resolved | |
5e7ed085 | 199 | flags.push((name, Some(self.tcx.type_of(def.did()).to_string()))); |
17df50a5 XL |
200 | } |
201 | } | |
cdc7bbd5 | 202 | } |
dfeec247 | 203 | |
5e7ed085 | 204 | if let Some(true) = self_ty.ty_adt_def().map(|def| def.did().is_local()) { |
cdc7bbd5 XL |
205 | flags.push((sym::crate_local, None)); |
206 | } | |
dfeec247 | 207 | |
cdc7bbd5 XL |
208 | // Allow targeting all integers using `{integral}`, even if the exact type was resolved |
209 | if self_ty.is_integral() { | |
210 | flags.push((sym::_Self, Some("{integral}".to_owned()))); | |
211 | } | |
6a06907d | 212 | |
5e7ed085 FG |
213 | if self_ty.is_array_slice() { |
214 | flags.push((sym::_Self, Some("&[]".to_owned()))); | |
215 | } | |
216 | ||
04454e1e FG |
217 | if self_ty.is_fn() { |
218 | let fn_sig = self_ty.fn_sig(self.tcx); | |
219 | let shortname = match fn_sig.unsafety() { | |
220 | hir::Unsafety::Normal => "fn", | |
221 | hir::Unsafety::Unsafe => "unsafe fn", | |
222 | }; | |
223 | flags.push((sym::_Self, Some(shortname.to_owned()))); | |
224 | } | |
225 | ||
226 | // Slices give us `[]`, `[{ty}]` | |
227 | if let ty::Slice(aty) = self_ty.kind() { | |
228 | flags.push((sym::_Self, Some("[]".to_string()))); | |
229 | if let Some(def) = aty.ty_adt_def() { | |
230 | // We also want to be able to select the slice's type's original | |
231 | // signature with no type arguments resolved | |
064997fb | 232 | flags.push((sym::_Self, Some(format!("[{}]", self.tcx.type_of(def.did()))))); |
04454e1e FG |
233 | } |
234 | if aty.is_integral() { | |
235 | flags.push((sym::_Self, Some("[{integral}]".to_string()))); | |
236 | } | |
237 | } | |
238 | ||
239 | // Arrays give us `[]`, `[{ty}; _]` and `[{ty}; N]` | |
cdc7bbd5 | 240 | if let ty::Array(aty, len) = self_ty.kind() { |
04454e1e | 241 | flags.push((sym::_Self, Some("[]".to_string()))); |
923072b8 | 242 | let len = len.kind().try_to_value().and_then(|v| v.try_to_machine_usize(self.tcx)); |
04454e1e FG |
243 | flags.push((sym::_Self, Some(format!("[{}; _]", aty)))); |
244 | if let Some(n) = len { | |
245 | flags.push((sym::_Self, Some(format!("[{}; {}]", aty, n)))); | |
246 | } | |
cdc7bbd5 XL |
247 | if let Some(def) = aty.ty_adt_def() { |
248 | // We also want to be able to select the array's type's original | |
249 | // signature with no type arguments resolved | |
064997fb FG |
250 | let def_ty = self.tcx.type_of(def.did()); |
251 | flags.push((sym::_Self, Some(format!("[{def_ty}; _]")))); | |
04454e1e | 252 | if let Some(n) = len { |
064997fb | 253 | flags.push((sym::_Self, Some(format!("[{def_ty}; {n}]")))); |
04454e1e FG |
254 | } |
255 | } | |
256 | if aty.is_integral() { | |
257 | flags.push((sym::_Self, Some("[{integral}; _]".to_string()))); | |
258 | if let Some(n) = len { | |
259 | flags.push((sym::_Self, Some(format!("[{{integral}}; {n}]")))); | |
260 | } | |
cdc7bbd5 | 261 | } |
dfeec247 | 262 | } |
f2b60f7d | 263 | if let ty::Dynamic(traits, _, _) = self_ty.kind() { |
cdc7bbd5 XL |
264 | for t in traits.iter() { |
265 | if let ty::ExistentialPredicate::Trait(trait_ref) = t.skip_binder() { | |
266 | flags.push((sym::_Self, Some(self.tcx.def_path_str(trait_ref.def_id)))) | |
267 | } | |
74b04a01 XL |
268 | } |
269 | } | |
cdc7bbd5 | 270 | }); |
dfeec247 | 271 | |
5e7ed085 | 272 | if let Ok(Some(command)) = OnUnimplementedDirective::of_item(self.tcx, def_id) { |
a2a8927a | 273 | command.evaluate(self.tcx, trait_ref, &flags) |
dfeec247 XL |
274 | } else { |
275 | OnUnimplementedNote::default() | |
276 | } | |
277 | } | |
278 | } |