2 ObligationCauseCode, OnUnimplementedDirective, OnUnimplementedNote, PredicateObligation,
4 use crate::infer::InferCtxt;
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;
12 use super::InferCtxtPrivExt;
14 crate trait InferCtxtExt<'tcx> {
18 trait_ref: ty::PolyTraitRef<'tcx>,
19 obligation: &PredicateObligation<'tcx>,
23 fn describe_enclosure(&self, hir_id: hir::HirId) -> Option<&'static str>;
25 fn on_unimplemented_note(
27 trait_ref: ty::PolyTraitRef<'tcx>,
28 obligation: &PredicateObligation<'tcx>,
29 ) -> OnUnimplementedNote;
32 impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
35 trait_ref: ty::PolyTraitRef<'tcx>,
36 obligation: &PredicateObligation<'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();
43 let mut self_match_impls = vec![];
44 let mut fuzzy_match_impls = vec![];
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);
50 let impl_self_ty = impl_trait_ref.self_ty();
52 if let Ok(..) = self.can_eq(param_env, trait_self_ty, impl_self_ty) {
53 self_match_impls.push(def_id);
56 trait_ref.substs.types().skip(1),
57 impl_trait_ref.substs.types().skip(1),
59 .all(|(u, v)| self.fuzzy_match_tys(u, v))
61 fuzzy_match_impls.push(def_id);
66 let impl_def_id = if self_match_impls.len() == 1 {
68 } else if fuzzy_match_impls.len() == 1 {
74 tcx.has_attr(impl_def_id, sym::rustc_on_unimplemented).then_some(impl_def_id)
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)?;
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",
91 hir::Node::TraitItem(hir::TraitItem {
92 kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body_id)),
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),
98 }) => self.describe_generator(*body_id).or_else(|| {
99 Some(match sig.header {
100 hir::FnHeader { asyncness: hir::IsAsync::Async, .. } => "an async method",
104 hir::Node::Expr(hir::Expr {
105 kind: hir::ExprKind::Closure(_is_move, _, body_id, _, gen_movability),
107 }) => self.describe_generator(*body_id).or_else(|| {
108 Some(if gen_movability.is_some() { "an async closure" } else { "a closure" })
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 }
118 fn on_unimplemented_note(
120 trait_ref: ty::PolyTraitRef<'tcx>,
121 obligation: &PredicateObligation<'tcx>,
122 ) -> OnUnimplementedNote {
124 self.impl_similar_to(trait_ref, obligation).unwrap_or_else(|| trait_ref.def_id());
125 let trait_ref = trait_ref.skip_binder();
127 let mut flags = vec![];
130 self.describe_enclosure(obligation.cause.body_id).map(|s| s.to_owned()),
133 match obligation.cause.code {
134 ObligationCauseCode::BuiltinDerivedObligation(..)
135 | ObligationCauseCode::ImplDerivedObligation(..)
136 | ObligationCauseCode::DerivedObligation(..) => {}
138 // this is a "direct", user-specified, rather than derived,
140 flags.push((sym::direct, None));
144 if let ObligationCauseCode::ItemObligation(item)
145 | ObligationCauseCode::BindingObligation(item, _) = obligation.cause.code
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.
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())));
158 if let Some((t, _)) = self.get_parent_trait_ref(&obligation.cause.code) {
159 flags.push((sym::parent_trait, Some(t)));
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))));
167 // Add all types without trimmed paths.
168 ty::print::with_no_trimmed_paths(|| {
169 let generics = self.tcx.generics_of(def_id);
170 let self_ty = trait_ref.self_ty();
171 // This is also included through the generics list as `Self`,
172 // but the parser won't allow you to use it
173 flags.push((sym::_Self, Some(self_ty.to_string())));
174 if let Some(def) = self_ty.ty_adt_def() {
175 // We also want to be able to select self's original
176 // signature with no type arguments resolved
177 flags.push((sym::_Self, Some(self.tcx.type_of(def.did).to_string())));
180 for param in generics.params.iter() {
181 let value = match param.kind {
182 GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => {
183 trait_ref.substs[param.index as usize].to_string()
185 GenericParamDefKind::Lifetime => continue,
187 let name = param.name;
188 flags.push((name, Some(value)));
190 if let GenericParamDefKind::Type { .. } = param.kind {
191 let param_ty = trait_ref.substs[param.index as usize].expect_ty();
192 if let Some(def) = param_ty.ty_adt_def() {
193 // We also want to be able to select the parameter's
194 // original signature with no type arguments resolved
195 flags.push((name, Some(self.tcx.type_of(def.did).to_string())));
200 if let Some(true) = self_ty.ty_adt_def().map(|def| def.did.is_local()) {
201 flags.push((sym::crate_local, None));
204 // Allow targeting all integers using `{integral}`, even if the exact type was resolved
205 if self_ty.is_integral() {
206 flags.push((sym::_Self, Some("{integral}".to_owned())));
209 if let ty::Array(aty, len) = self_ty.kind() {
210 flags.push((sym::_Self, Some("[]".to_owned())));
211 flags.push((sym::_Self, Some(format!("[{}]", aty))));
212 if let Some(def) = aty.ty_adt_def() {
213 // We also want to be able to select the array's type's original
214 // signature with no type arguments resolved
215 let type_string = self.tcx.type_of(def.did).to_string();
216 flags.push((sym::_Self, Some(format!("[{}]", type_string))));
218 let len = len.val.try_to_value().and_then(|v| v.try_to_machine_usize(self.tcx));
219 let string = match len {
220 Some(n) => format!("[{}; {}]", type_string, n),
221 None => format!("[{}; _]", type_string),
223 flags.push((sym::_Self, Some(string)));
226 if let ty::Dynamic(traits, _) = self_ty.kind() {
227 for t in traits.iter() {
228 if let ty::ExistentialPredicate::Trait(trait_ref) = t.skip_binder() {
229 flags.push((sym::_Self, Some(self.tcx.def_path_str(trait_ref.def_id))))
235 if let Ok(Some(command)) =
236 OnUnimplementedDirective::of_item(self.tcx, trait_ref.def_id, def_id)
238 command.evaluate(self.tcx, trait_ref, &flags[..])
240 OnUnimplementedNote::default()