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, false).is_some())
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![(
129 self.describe_enclosure(obligation.cause.body_id).map(|s| s.to_owned()),
132 match obligation.cause.code() {
133 ObligationCauseCode::BuiltinDerivedObligation(..)
134 | ObligationCauseCode::ImplDerivedObligation(..)
135 | ObligationCauseCode::DerivedObligation(..) => {}
137 // this is a "direct", user-specified, rather than derived,
139 flags.push((sym::direct, None));
143 if let ObligationCauseCode::ItemObligation(item)
144 | ObligationCauseCode::BindingObligation(item, _) = *obligation.cause.code()
146 // FIXME: maybe also have some way of handling methods
147 // from other traits? That would require name resolution,
148 // which we might want to be some sort of hygienic.
150 // Currently I'm leaving it for what I need for `try`.
151 if self.tcx.trait_of_item(item) == Some(trait_ref.def_id) {
152 let method = self.tcx.item_name(item);
153 flags.push((sym::from_method, None));
154 flags.push((sym::from_method, Some(method.to_string())));
158 if let Some(k) = obligation.cause.span.desugaring_kind() {
159 flags.push((sym::from_desugaring, None));
160 flags.push((sym::from_desugaring, Some(format!("{:?}", k))));
163 // Add all types without trimmed paths.
164 ty::print::with_no_trimmed_paths(|| {
165 let generics = self.tcx.generics_of(def_id);
166 let self_ty = trait_ref.self_ty();
167 // This is also included through the generics list as `Self`,
168 // but the parser won't allow you to use it
169 flags.push((sym::_Self, Some(self_ty.to_string())));
170 if let Some(def) = self_ty.ty_adt_def() {
171 // We also want to be able to select self's original
172 // signature with no type arguments resolved
173 flags.push((sym::_Self, Some(self.tcx.type_of(def.did).to_string())));
176 for param in generics.params.iter() {
177 let value = match param.kind {
178 GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => {
179 trait_ref.substs[param.index as usize].to_string()
181 GenericParamDefKind::Lifetime => continue,
183 let name = param.name;
184 flags.push((name, Some(value)));
186 if let GenericParamDefKind::Type { .. } = param.kind {
187 let param_ty = trait_ref.substs[param.index as usize].expect_ty();
188 if let Some(def) = param_ty.ty_adt_def() {
189 // We also want to be able to select the parameter's
190 // original signature with no type arguments resolved
191 flags.push((name, Some(self.tcx.type_of(def.did).to_string())));
196 if let Some(true) = self_ty.ty_adt_def().map(|def| def.did.is_local()) {
197 flags.push((sym::crate_local, None));
200 // Allow targeting all integers using `{integral}`, even if the exact type was resolved
201 if self_ty.is_integral() {
202 flags.push((sym::_Self, Some("{integral}".to_owned())));
205 if let ty::Array(aty, len) = self_ty.kind() {
206 flags.push((sym::_Self, Some("[]".to_owned())));
207 flags.push((sym::_Self, Some(format!("[{}]", aty))));
208 if let Some(def) = aty.ty_adt_def() {
209 // We also want to be able to select the array's type's original
210 // signature with no type arguments resolved
211 let type_string = self.tcx.type_of(def.did).to_string();
212 flags.push((sym::_Self, Some(format!("[{}]", type_string))));
215 len.val().try_to_value().and_then(|v| v.try_to_machine_usize(self.tcx));
216 let string = match len {
217 Some(n) => format!("[{}; {}]", type_string, n),
218 None => format!("[{}; _]", type_string),
220 flags.push((sym::_Self, Some(string)));
223 if let ty::Dynamic(traits, _) = self_ty.kind() {
224 for t in traits.iter() {
225 if let ty::ExistentialPredicate::Trait(trait_ref) = t.skip_binder() {
226 flags.push((sym::_Self, Some(self.tcx.def_path_str(trait_ref.def_id))))
232 if let Ok(Some(command)) =
233 OnUnimplementedDirective::of_item(self.tcx, trait_ref.def_id, def_id)
235 command.evaluate(self.tcx, trait_ref, &flags)
237 OnUnimplementedNote::default()