| use super::{ |
| ObligationCauseCode, OnUnimplementedDirective, OnUnimplementedNote, PredicateObligation, |
| }; |
| use crate::infer::InferCtxt; |
| use rustc_hir as hir; |
| use rustc_hir::def_id::DefId; |
| use rustc_middle::ty::subst::Subst; |
| use rustc_middle::ty::{self, GenericParamDefKind}; |
| use rustc_span::symbol::sym; |
| |
| use super::InferCtxtPrivExt; |
| |
| crate trait InferCtxtExt<'tcx> { |
| /*private*/ |
| fn impl_similar_to( |
| &self, |
| trait_ref: ty::PolyTraitRef<'tcx>, |
| obligation: &PredicateObligation<'tcx>, |
| ) -> Option<DefId>; |
| |
| /*private*/ |
| fn describe_enclosure(&self, hir_id: hir::HirId) -> Option<&'static str>; |
| |
| fn on_unimplemented_note( |
| &self, |
| trait_ref: ty::PolyTraitRef<'tcx>, |
| obligation: &PredicateObligation<'tcx>, |
| ) -> OnUnimplementedNote; |
| } |
| |
| impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { |
| fn impl_similar_to( |
| &self, |
| trait_ref: ty::PolyTraitRef<'tcx>, |
| obligation: &PredicateObligation<'tcx>, |
| ) -> Option<DefId> { |
| let tcx = self.tcx; |
| let param_env = obligation.param_env; |
| let trait_ref = tcx.erase_late_bound_regions(&trait_ref); |
| let trait_self_ty = trait_ref.self_ty(); |
| |
| let mut self_match_impls = vec![]; |
| let mut fuzzy_match_impls = vec![]; |
| |
| self.tcx.for_each_relevant_impl(trait_ref.def_id, trait_self_ty, |def_id| { |
| let impl_substs = self.fresh_substs_for_item(obligation.cause.span, def_id); |
| let impl_trait_ref = tcx.impl_trait_ref(def_id).unwrap().subst(tcx, impl_substs); |
| |
| let impl_self_ty = impl_trait_ref.self_ty(); |
| |
| if let Ok(..) = self.can_eq(param_env, trait_self_ty, impl_self_ty) { |
| self_match_impls.push(def_id); |
| |
| if trait_ref |
| .substs |
| .types() |
| .skip(1) |
| .zip(impl_trait_ref.substs.types().skip(1)) |
| .all(|(u, v)| self.fuzzy_match_tys(u, v)) |
| { |
| fuzzy_match_impls.push(def_id); |
| } |
| } |
| }); |
| |
| let impl_def_id = if self_match_impls.len() == 1 { |
| self_match_impls[0] |
| } else if fuzzy_match_impls.len() == 1 { |
| fuzzy_match_impls[0] |
| } else { |
| return None; |
| }; |
| |
| tcx.has_attr(impl_def_id, sym::rustc_on_unimplemented).then_some(impl_def_id) |
| } |
| |
| /// Used to set on_unimplemented's `ItemContext` |
| /// to be the enclosing (async) block/function/closure |
| fn describe_enclosure(&self, hir_id: hir::HirId) -> Option<&'static str> { |
| let hir = &self.tcx.hir(); |
| let node = hir.find(hir_id)?; |
| match &node { |
| hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, _, body_id), .. }) => { |
| self.describe_generator(*body_id).or_else(|| { |
| Some(match sig.header { |
| hir::FnHeader { asyncness: hir::IsAsync::Async, .. } => "an async function", |
| _ => "a function", |
| }) |
| }) |
| } |
| hir::Node::TraitItem(hir::TraitItem { |
| kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body_id)), |
| .. |
| }) => self.describe_generator(*body_id).or_else(|| Some("a trait method")), |
| hir::Node::ImplItem(hir::ImplItem { |
| kind: hir::ImplItemKind::Fn(sig, body_id), |
| .. |
| }) => self.describe_generator(*body_id).or_else(|| { |
| Some(match sig.header { |
| hir::FnHeader { asyncness: hir::IsAsync::Async, .. } => "an async method", |
| _ => "a method", |
| }) |
| }), |
| hir::Node::Expr(hir::Expr { |
| kind: hir::ExprKind::Closure(_is_move, _, body_id, _, gen_movability), |
| .. |
| }) => self.describe_generator(*body_id).or_else(|| { |
| Some(if gen_movability.is_some() { "an async closure" } else { "a closure" }) |
| }), |
| hir::Node::Expr(hir::Expr { .. }) => { |
| let parent_hid = hir.get_parent_node(hir_id); |
| if parent_hid != hir_id { self.describe_enclosure(parent_hid) } else { None } |
| } |
| _ => None, |
| } |
| } |
| |
| fn on_unimplemented_note( |
| &self, |
| trait_ref: ty::PolyTraitRef<'tcx>, |
| obligation: &PredicateObligation<'tcx>, |
| ) -> OnUnimplementedNote { |
| let def_id = |
| self.impl_similar_to(trait_ref, obligation).unwrap_or_else(|| trait_ref.def_id()); |
| let trait_ref = trait_ref.skip_binder(); |
| |
| let mut flags = vec![]; |
| flags.push(( |
| sym::item_context, |
| self.describe_enclosure(obligation.cause.body_id).map(|s| s.to_owned()), |
| )); |
| |
| match obligation.cause.code { |
| ObligationCauseCode::BuiltinDerivedObligation(..) |
| | ObligationCauseCode::ImplDerivedObligation(..) |
| | ObligationCauseCode::DerivedObligation(..) => {} |
| _ => { |
| // this is a "direct", user-specified, rather than derived, |
| // obligation. |
| flags.push((sym::direct, None)); |
| } |
| } |
| |
| if let ObligationCauseCode::ItemObligation(item) |
| | ObligationCauseCode::BindingObligation(item, _) = obligation.cause.code |
| { |
| // FIXME: maybe also have some way of handling methods |
| // from other traits? That would require name resolution, |
| // which we might want to be some sort of hygienic. |
| // |
| // Currently I'm leaving it for what I need for `try`. |
| if self.tcx.trait_of_item(item) == Some(trait_ref.def_id) { |
| let method = self.tcx.item_name(item); |
| flags.push((sym::from_method, None)); |
| flags.push((sym::from_method, Some(method.to_string()))); |
| } |
| } |
| if let Some((t, _)) = self.get_parent_trait_ref(&obligation.cause.code) { |
| flags.push((sym::parent_trait, Some(t))); |
| } |
| |
| if let Some(k) = obligation.cause.span.desugaring_kind() { |
| flags.push((sym::from_desugaring, None)); |
| flags.push((sym::from_desugaring, Some(format!("{:?}", k)))); |
| } |
| let generics = self.tcx.generics_of(def_id); |
| let self_ty = trait_ref.self_ty(); |
| // This is also included through the generics list as `Self`, |
| // but the parser won't allow you to use it |
| flags.push((sym::_Self, Some(self_ty.to_string()))); |
| if let Some(def) = self_ty.ty_adt_def() { |
| // We also want to be able to select self's original |
| // signature with no type arguments resolved |
| flags.push((sym::_Self, Some(self.tcx.type_of(def.did).to_string()))); |
| } |
| |
| for param in generics.params.iter() { |
| let value = match param.kind { |
| GenericParamDefKind::Type { .. } | GenericParamDefKind::Const => { |
| trait_ref.substs[param.index as usize].to_string() |
| } |
| GenericParamDefKind::Lifetime => continue, |
| }; |
| let name = param.name; |
| flags.push((name, Some(value))); |
| } |
| |
| if let Some(true) = self_ty.ty_adt_def().map(|def| def.did.is_local()) { |
| flags.push((sym::crate_local, None)); |
| } |
| |
| // Allow targeting all integers using `{integral}`, even if the exact type was resolved |
| if self_ty.is_integral() { |
| flags.push((sym::_Self, Some("{integral}".to_owned()))); |
| } |
| |
| if let ty::Array(aty, len) = self_ty.kind { |
| flags.push((sym::_Self, Some("[]".to_owned()))); |
| flags.push((sym::_Self, Some(format!("[{}]", aty)))); |
| if let Some(def) = aty.ty_adt_def() { |
| // We also want to be able to select the array's type's original |
| // signature with no type arguments resolved |
| flags.push(( |
| sym::_Self, |
| Some(format!("[{}]", self.tcx.type_of(def.did).to_string())), |
| )); |
| let tcx = self.tcx; |
| if let Some(len) = len.try_eval_usize(tcx, ty::ParamEnv::empty()) { |
| flags.push(( |
| sym::_Self, |
| Some(format!("[{}; {}]", self.tcx.type_of(def.did).to_string(), len)), |
| )); |
| } else { |
| flags.push(( |
| sym::_Self, |
| Some(format!("[{}; _]", self.tcx.type_of(def.did).to_string())), |
| )); |
| } |
| } |
| } |
| if let ty::Dynamic(traits, _) = self_ty.kind { |
| for t in traits.skip_binder() { |
| if let ty::ExistentialPredicate::Trait(trait_ref) = t { |
| flags.push((sym::_Self, Some(self.tcx.def_path_str(trait_ref.def_id)))) |
| } |
| } |
| } |
| |
| if let Ok(Some(command)) = |
| OnUnimplementedDirective::of_item(self.tcx, trait_ref.def_id, def_id) |
| { |
| command.evaluate(self.tcx, trait_ref, &flags[..]) |
| } else { |
| OnUnimplementedNote::default() |
| } |
| } |
| } |