|
| 1 | +use super::{ |
| 2 | + ObligationCauseCode, OnUnimplementedDirective, OnUnimplementedNote, PredicateObligation, |
| 3 | +}; |
| 4 | +use crate::infer::InferCtxt; |
| 5 | +use crate::ty::subst::Subst; |
| 6 | +use crate::ty::{self, GenericParamDefKind}; |
| 7 | +use rustc_hir as hir; |
| 8 | +use rustc_hir::def_id::DefId; |
| 9 | +use rustc_span::symbol::sym; |
| 10 | + |
| 11 | +impl<'a, 'tcx> InferCtxt<'a, 'tcx> { |
| 12 | + fn impl_similar_to( |
| 13 | + &self, |
| 14 | + trait_ref: ty::PolyTraitRef<'tcx>, |
| 15 | + obligation: &PredicateObligation<'tcx>, |
| 16 | + ) -> Option<DefId> { |
| 17 | + let tcx = self.tcx; |
| 18 | + let param_env = obligation.param_env; |
| 19 | + let trait_ref = tcx.erase_late_bound_regions(&trait_ref); |
| 20 | + let trait_self_ty = trait_ref.self_ty(); |
| 21 | + |
| 22 | + let mut self_match_impls = vec![]; |
| 23 | + let mut fuzzy_match_impls = vec![]; |
| 24 | + |
| 25 | + self.tcx.for_each_relevant_impl(trait_ref.def_id, trait_self_ty, |def_id| { |
| 26 | + let impl_substs = self.fresh_substs_for_item(obligation.cause.span, def_id); |
| 27 | + let impl_trait_ref = tcx.impl_trait_ref(def_id).unwrap().subst(tcx, impl_substs); |
| 28 | + |
| 29 | + let impl_self_ty = impl_trait_ref.self_ty(); |
| 30 | + |
| 31 | + if let Ok(..) = self.can_eq(param_env, trait_self_ty, impl_self_ty) { |
| 32 | + self_match_impls.push(def_id); |
| 33 | + |
| 34 | + if trait_ref |
| 35 | + .substs |
| 36 | + .types() |
| 37 | + .skip(1) |
| 38 | + .zip(impl_trait_ref.substs.types().skip(1)) |
| 39 | + .all(|(u, v)| self.fuzzy_match_tys(u, v)) |
| 40 | + { |
| 41 | + fuzzy_match_impls.push(def_id); |
| 42 | + } |
| 43 | + } |
| 44 | + }); |
| 45 | + |
| 46 | + let impl_def_id = if self_match_impls.len() == 1 { |
| 47 | + self_match_impls[0] |
| 48 | + } else if fuzzy_match_impls.len() == 1 { |
| 49 | + fuzzy_match_impls[0] |
| 50 | + } else { |
| 51 | + return None; |
| 52 | + }; |
| 53 | + |
| 54 | + tcx.has_attr(impl_def_id, sym::rustc_on_unimplemented).then_some(impl_def_id) |
| 55 | + } |
| 56 | + |
| 57 | + /// Used to set on_unimplemented's `ItemContext` |
| 58 | + /// to be the enclosing (async) block/function/closure |
| 59 | + fn describe_enclosure(&self, hir_id: hir::HirId) -> Option<&'static str> { |
| 60 | + let hir = &self.tcx.hir(); |
| 61 | + let node = hir.find(hir_id)?; |
| 62 | + if let hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, _, body_id), .. }) = &node { |
| 63 | + self.describe_generator(*body_id).or_else(|| { |
| 64 | + Some(if let hir::FnHeader { asyncness: hir::IsAsync::Async, .. } = sig.header { |
| 65 | + "an async function" |
| 66 | + } else { |
| 67 | + "a function" |
| 68 | + }) |
| 69 | + }) |
| 70 | + } else if let hir::Node::Expr(hir::Expr { |
| 71 | + kind: hir::ExprKind::Closure(_is_move, _, body_id, _, gen_movability), |
| 72 | + .. |
| 73 | + }) = &node |
| 74 | + { |
| 75 | + self.describe_generator(*body_id).or_else(|| { |
| 76 | + Some(if gen_movability.is_some() { "an async closure" } else { "a closure" }) |
| 77 | + }) |
| 78 | + } else if let hir::Node::Expr(hir::Expr { .. }) = &node { |
| 79 | + let parent_hid = hir.get_parent_node(hir_id); |
| 80 | + if parent_hid != hir_id { |
| 81 | + return self.describe_enclosure(parent_hid); |
| 82 | + } else { |
| 83 | + None |
| 84 | + } |
| 85 | + } else { |
| 86 | + None |
| 87 | + } |
| 88 | + } |
| 89 | + |
| 90 | + crate fn on_unimplemented_note( |
| 91 | + &self, |
| 92 | + trait_ref: ty::PolyTraitRef<'tcx>, |
| 93 | + obligation: &PredicateObligation<'tcx>, |
| 94 | + ) -> OnUnimplementedNote { |
| 95 | + let def_id = |
| 96 | + self.impl_similar_to(trait_ref, obligation).unwrap_or_else(|| trait_ref.def_id()); |
| 97 | + let trait_ref = *trait_ref.skip_binder(); |
| 98 | + |
| 99 | + let mut flags = vec![]; |
| 100 | + flags.push(( |
| 101 | + sym::item_context, |
| 102 | + self.describe_enclosure(obligation.cause.body_id).map(|s| s.to_owned()), |
| 103 | + )); |
| 104 | + |
| 105 | + match obligation.cause.code { |
| 106 | + ObligationCauseCode::BuiltinDerivedObligation(..) |
| 107 | + | ObligationCauseCode::ImplDerivedObligation(..) => {} |
| 108 | + _ => { |
| 109 | + // this is a "direct", user-specified, rather than derived, |
| 110 | + // obligation. |
| 111 | + flags.push((sym::direct, None)); |
| 112 | + } |
| 113 | + } |
| 114 | + |
| 115 | + if let ObligationCauseCode::ItemObligation(item) = obligation.cause.code { |
| 116 | + // FIXME: maybe also have some way of handling methods |
| 117 | + // from other traits? That would require name resolution, |
| 118 | + // which we might want to be some sort of hygienic. |
| 119 | + // |
| 120 | + // Currently I'm leaving it for what I need for `try`. |
| 121 | + if self.tcx.trait_of_item(item) == Some(trait_ref.def_id) { |
| 122 | + let method = self.tcx.item_name(item); |
| 123 | + flags.push((sym::from_method, None)); |
| 124 | + flags.push((sym::from_method, Some(method.to_string()))); |
| 125 | + } |
| 126 | + } |
| 127 | + if let Some((t, _)) = self.get_parent_trait_ref(&obligation.cause.code) { |
| 128 | + flags.push((sym::parent_trait, Some(t))); |
| 129 | + } |
| 130 | + |
| 131 | + if let Some(k) = obligation.cause.span.desugaring_kind() { |
| 132 | + flags.push((sym::from_desugaring, None)); |
| 133 | + flags.push((sym::from_desugaring, Some(format!("{:?}", k)))); |
| 134 | + } |
| 135 | + let generics = self.tcx.generics_of(def_id); |
| 136 | + let self_ty = trait_ref.self_ty(); |
| 137 | + // This is also included through the generics list as `Self`, |
| 138 | + // but the parser won't allow you to use it |
| 139 | + flags.push((sym::_Self, Some(self_ty.to_string()))); |
| 140 | + if let Some(def) = self_ty.ty_adt_def() { |
| 141 | + // We also want to be able to select self's original |
| 142 | + // signature with no type arguments resolved |
| 143 | + flags.push((sym::_Self, Some(self.tcx.type_of(def.did).to_string()))); |
| 144 | + } |
| 145 | + |
| 146 | + for param in generics.params.iter() { |
| 147 | + let value = match param.kind { |
| 148 | + GenericParamDefKind::Type { .. } | GenericParamDefKind::Const => { |
| 149 | + trait_ref.substs[param.index as usize].to_string() |
| 150 | + } |
| 151 | + GenericParamDefKind::Lifetime => continue, |
| 152 | + }; |
| 153 | + let name = param.name; |
| 154 | + flags.push((name, Some(value))); |
| 155 | + } |
| 156 | + |
| 157 | + if let Some(true) = self_ty.ty_adt_def().map(|def| def.did.is_local()) { |
| 158 | + flags.push((sym::crate_local, None)); |
| 159 | + } |
| 160 | + |
| 161 | + // Allow targeting all integers using `{integral}`, even if the exact type was resolved |
| 162 | + if self_ty.is_integral() { |
| 163 | + flags.push((sym::_Self, Some("{integral}".to_owned()))); |
| 164 | + } |
| 165 | + |
| 166 | + if let ty::Array(aty, len) = self_ty.kind { |
| 167 | + flags.push((sym::_Self, Some("[]".to_owned()))); |
| 168 | + flags.push((sym::_Self, Some(format!("[{}]", aty)))); |
| 169 | + if let Some(def) = aty.ty_adt_def() { |
| 170 | + // We also want to be able to select the array's type's original |
| 171 | + // signature with no type arguments resolved |
| 172 | + flags.push(( |
| 173 | + sym::_Self, |
| 174 | + Some(format!("[{}]", self.tcx.type_of(def.did).to_string())), |
| 175 | + )); |
| 176 | + let tcx = self.tcx; |
| 177 | + if let Some(len) = len.try_eval_usize(tcx, ty::ParamEnv::empty()) { |
| 178 | + flags.push(( |
| 179 | + sym::_Self, |
| 180 | + Some(format!("[{}; {}]", self.tcx.type_of(def.did).to_string(), len)), |
| 181 | + )); |
| 182 | + } else { |
| 183 | + flags.push(( |
| 184 | + sym::_Self, |
| 185 | + Some(format!("[{}; _]", self.tcx.type_of(def.did).to_string())), |
| 186 | + )); |
| 187 | + } |
| 188 | + } |
| 189 | + } |
| 190 | + |
| 191 | + if let Ok(Some(command)) = |
| 192 | + OnUnimplementedDirective::of_item(self.tcx, trait_ref.def_id, def_id) |
| 193 | + { |
| 194 | + command.evaluate(self.tcx, trait_ref, &flags[..]) |
| 195 | + } else { |
| 196 | + OnUnimplementedNote::default() |
| 197 | + } |
| 198 | + } |
| 199 | +} |
0 commit comments