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 use std::iter; 11 12 use super::InferCtxtPrivExt; 13 14 crate trait InferCtxtExt<'tcx> { 15 /*private*/ impl_similar_to( &self, trait_ref: ty::PolyTraitRef<'tcx>, obligation: &PredicateObligation<'tcx>, ) -> Option<DefId>16 fn impl_similar_to( 17 &self, 18 trait_ref: ty::PolyTraitRef<'tcx>, 19 obligation: &PredicateObligation<'tcx>, 20 ) -> Option<DefId>; 21 22 /*private*/ describe_enclosure(&self, hir_id: hir::HirId) -> Option<&'static str>23 fn describe_enclosure(&self, hir_id: hir::HirId) -> Option<&'static str>; 24 on_unimplemented_note( &self, trait_ref: ty::PolyTraitRef<'tcx>, obligation: &PredicateObligation<'tcx>, ) -> OnUnimplementedNote25 fn on_unimplemented_note( 26 &self, 27 trait_ref: ty::PolyTraitRef<'tcx>, 28 obligation: &PredicateObligation<'tcx>, 29 ) -> OnUnimplementedNote; 30 } 31 32 impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { impl_similar_to( &self, trait_ref: ty::PolyTraitRef<'tcx>, obligation: &PredicateObligation<'tcx>, ) -> Option<DefId>33 fn impl_similar_to( 34 &self, 35 trait_ref: ty::PolyTraitRef<'tcx>, 36 obligation: &PredicateObligation<'tcx>, 37 ) -> Option<DefId> { 38 let tcx = self.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(); 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); 48 let impl_trait_ref = tcx.impl_trait_ref(def_id).unwrap().subst(tcx, impl_substs); 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) { 53 self_match_impls.push(def_id); 54 55 if iter::zip( 56 trait_ref.substs.types().skip(1), 57 impl_trait_ref.substs.types().skip(1), 58 ) 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 describe_enclosure(&self, hir_id: hir::HirId) -> Option<&'static str>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 on_unimplemented_note( &self, trait_ref: ty::PolyTraitRef<'tcx>, obligation: &PredicateObligation<'tcx>, ) -> OnUnimplementedNote118 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 sym::ItemContext, 129 self.describe_enclosure(obligation.cause.body_id).map(|s| s.to_owned()), 130 )]; 131 132 match obligation.cause.code { 133 ObligationCauseCode::BuiltinDerivedObligation(..) 134 | ObligationCauseCode::ImplDerivedObligation(..) 135 | ObligationCauseCode::DerivedObligation(..) => {} 136 _ => { 137 // this is a "direct", user-specified, rather than derived, 138 // obligation. 139 flags.push((sym::direct, None)); 140 } 141 } 142 143 if let ObligationCauseCode::ItemObligation(item) 144 | ObligationCauseCode::BindingObligation(item, _) = obligation.cause.code 145 { 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. 149 // 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()))); 155 } 156 } 157 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)))); 161 } 162 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()))); 174 } 175 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() 180 } 181 GenericParamDefKind::Lifetime => continue, 182 }; 183 let name = param.name; 184 flags.push((name, Some(value))); 185 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()))); 192 } 193 } 194 } 195 196 if let Some(true) = self_ty.ty_adt_def().map(|def| def.did.is_local()) { 197 flags.push((sym::crate_local, None)); 198 } 199 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()))); 203 } 204 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)))); 213 214 let len = len.val.try_to_value().and_then(|v| v.try_to_machine_usize(self.tcx)); 215 let string = match len { 216 Some(n) => format!("[{}; {}]", type_string, n), 217 None => format!("[{}; _]", type_string), 218 }; 219 flags.push((sym::_Self, Some(string))); 220 } 221 } 222 if let ty::Dynamic(traits, _) = self_ty.kind() { 223 for t in traits.iter() { 224 if let ty::ExistentialPredicate::Trait(trait_ref) = t.skip_binder() { 225 flags.push((sym::_Self, Some(self.tcx.def_path_str(trait_ref.def_id)))) 226 } 227 } 228 } 229 }); 230 231 if let Ok(Some(command)) = 232 OnUnimplementedDirective::of_item(self.tcx, trait_ref.def_id, def_id) 233 { 234 command.evaluate(self.tcx, trait_ref, &flags[..]) 235 } else { 236 OnUnimplementedNote::default() 237 } 238 } 239 } 240