diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:19:41 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:19:41 +0000 |
commit | 4f9fe856a25ab29345b90e7725509e9ee38a37be (patch) | |
tree | e4ffd8a9374cae7b21f7cbfb352927e0e074aff6 /compiler/rustc_hir_typeck/src/method/probe.rs | |
parent | Adding upstream version 1.68.2+dfsg1. (diff) | |
download | rustc-upstream/1.69.0+dfsg1.tar.xz rustc-upstream/1.69.0+dfsg1.zip |
Adding upstream version 1.69.0+dfsg1.upstream/1.69.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | compiler/rustc_hir_typeck/src/method/probe.rs | 282 |
1 files changed, 129 insertions, 153 deletions
diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index a24814313..3bef5cfcd 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -9,24 +9,23 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::DefKind; +use rustc_hir_analysis::astconv::InferCtxtExt as _; use rustc_hir_analysis::autoderef::{self, Autoderef}; use rustc_infer::infer::canonical::OriginalQueryValues; use rustc_infer::infer::canonical::{Canonical, QueryResponse}; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::{self, InferOk, TyCtxtInferExt}; -use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; use rustc_middle::middle::stability; use rustc_middle::ty::fast_reject::{simplify_type, TreatParams}; use rustc_middle::ty::AssocItem; use rustc_middle::ty::GenericParamDefKind; use rustc_middle::ty::ToPredicate; -use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeFoldable, TypeVisitable}; +use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeFoldable, TypeVisitableExt}; use rustc_middle::ty::{InternalSubsts, SubstsRef}; use rustc_session::lint; use rustc_span::def_id::DefId; use rustc_span::def_id::LocalDefId; -use rustc_span::lev_distance::{ - find_best_match_for_name_with_substrings, lev_distance_with_substrings, +use rustc_span::edit_distance::{ + edit_distance_with_substrings, find_best_match_for_name_with_substrings, }; use rustc_span::symbol::sym; use rustc_span::{symbol::Ident, Span, Symbol, DUMMY_SP}; @@ -70,7 +69,7 @@ struct ProbeContext<'a, 'tcx> { impl_dups: FxHashSet<DefId>, /// When probing for names, include names that are close to the - /// requested name (by Levenshtein distance) + /// requested name (by edit distance) allow_similar_names: bool, /// Some(candidate) if there is a private candidate @@ -461,7 +460,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { static_candidates: Vec::new(), unsatisfied_predicates: Vec::new(), out_of_scope_traits: Vec::new(), - lev_candidate: None, + similar_candidate: None, mode, })); } @@ -508,16 +507,16 @@ fn method_autoderef_steps<'tcx>( let (ref infcx, goal, inference_vars) = tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &goal); let ParamEnvAnd { param_env, value: self_ty } = goal; - let mut autoderef = Autoderef::new(infcx, param_env, hir::CRATE_HIR_ID, DUMMY_SP, self_ty) - .include_raw_pointers() - .silence_errors(); + let mut autoderef = + Autoderef::new(infcx, param_env, hir::def_id::CRATE_DEF_ID, DUMMY_SP, self_ty) + .include_raw_pointers() + .silence_errors(); let mut reached_raw_pointer = false; let mut steps: Vec<_> = autoderef .by_ref() .map(|(ty, d)| { let step = CandidateStep { - self_ty: infcx - .make_query_response_ignoring_pending_obligations(inference_vars.clone(), ty), + self_ty: infcx.make_query_response_ignoring_pending_obligations(inference_vars, ty), autoderefs: d, from_unsafe_deref: reached_raw_pointer, unsize: false, @@ -610,10 +609,9 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { fn push_candidate(&mut self, candidate: Candidate<'tcx>, is_inherent: bool) { let is_accessible = if let Some(name) = self.method_name { let item = candidate.item; - let def_scope = self - .tcx - .adjust_ident_and_get_scope(name, item.container_id(self.tcx), self.body_id) - .1; + let hir_id = self.tcx.hir().local_def_id_to_hir_id(self.body_id); + let def_scope = + self.tcx.adjust_ident_and_get_scope(name, item.container_id(self.tcx), hir_id).1; item.visibility(self.tcx).is_accessible_from(def_scope, self.tcx) } else { true @@ -736,7 +734,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { debug!("impl_ty: {:?}", impl_ty); // Determine the receiver type that the method itself expects. - let (xform_self_ty, xform_ret_ty) = self.xform_self_ty(&item, impl_ty, impl_substs); + let (xform_self_ty, xform_ret_ty) = self.xform_self_ty(item, impl_ty, impl_substs); debug!("xform_self_ty: {:?}, xform_ret_ty: {:?}", xform_self_ty, xform_ret_ty); // We can't use normalize_associated_types_in as it will pollute the @@ -797,7 +795,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let new_trait_ref = this.erase_late_bound_regions(new_trait_ref); let (xform_self_ty, xform_ret_ty) = - this.xform_self_ty(&item, new_trait_ref.self_ty(), new_trait_ref.substs); + this.xform_self_ty(item, new_trait_ref.self_ty(), new_trait_ref.substs); this.push_candidate( Candidate { xform_self_ty, @@ -827,6 +825,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } ty::PredicateKind::Subtype(..) + | ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) | ty::PredicateKind::Coerce(..) | ty::PredicateKind::Clause(ty::Clause::Projection(..)) | ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..)) @@ -837,6 +836,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) | ty::PredicateKind::Ambiguous + | ty::PredicateKind::AliasEq(..) | ty::PredicateKind::TypeWellFormedFromEnv(..) => None, } }); @@ -845,7 +845,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let trait_ref = this.erase_late_bound_regions(poly_trait_ref); let (xform_self_ty, xform_ret_ty) = - this.xform_self_ty(&item, trait_ref.self_ty(), trait_ref.substs); + this.xform_self_ty(item, trait_ref.self_ty(), trait_ref.substs); // Because this trait derives from a where-clause, it // should not contain any inference variables or other @@ -916,31 +916,27 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { fn matches_return_type( &self, - method: &ty::AssocItem, + method: ty::AssocItem, self_ty: Option<Ty<'tcx>>, expected: Ty<'tcx>, ) -> bool { match method.kind { - ty::AssocKind::Fn => { - let fty = self.tcx.bound_fn_sig(method.def_id); - self.probe(|_| { - let substs = self.fresh_substs_for_item(self.span, method.def_id); - let fty = fty.subst(self.tcx, substs); - let fty = - self.replace_bound_vars_with_fresh_vars(self.span, infer::FnCall, fty); - - if let Some(self_ty) = self_ty { - if self - .at(&ObligationCause::dummy(), self.param_env) - .sup(fty.inputs()[0], self_ty) - .is_err() - { - return false; - } + ty::AssocKind::Fn => self.probe(|_| { + let substs = self.fresh_substs_for_item(self.span, method.def_id); + let fty = self.tcx.fn_sig(method.def_id).subst(self.tcx, substs); + let fty = self.instantiate_binder_with_fresh_vars(self.span, infer::FnCall, fty); + + if let Some(self_ty) = self_ty { + if self + .at(&ObligationCause::dummy(), self.param_env) + .sup(fty.inputs()[0], self_ty) + .is_err() + { + return false; } - self.can_sub(self.param_env, fty.output(), expected).is_ok() - }) - } + } + self.can_sub(self.param_env, fty.output(), expected) + }), _ => false, } } @@ -955,24 +951,35 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let trait_ref = self.tcx.mk_trait_ref(trait_def_id, trait_substs); if self.tcx.is_trait_alias(trait_def_id) { - // For trait aliases, assume all supertraits are relevant. - let bounds = iter::once(ty::Binder::dummy(trait_ref)); - self.elaborate_bounds(bounds, |this, new_trait_ref, item| { - let new_trait_ref = this.erase_late_bound_regions(new_trait_ref); - - let (xform_self_ty, xform_ret_ty) = - this.xform_self_ty(&item, new_trait_ref.self_ty(), new_trait_ref.substs); - this.push_candidate( - Candidate { - xform_self_ty, - xform_ret_ty, - item, - import_ids: import_ids.clone(), - kind: TraitCandidate(new_trait_ref), - }, - false, - ); - }); + // For trait aliases, recursively assume all explicitly named traits are relevant + for expansion in traits::expand_trait_aliases( + self.tcx, + iter::once((ty::Binder::dummy(trait_ref), self.span)), + ) { + let bound_trait_ref = expansion.trait_ref(); + for item in self.impl_or_trait_item(bound_trait_ref.def_id()) { + if !self.has_applicable_self(&item) { + self.record_static_candidate(CandidateSource::Trait( + bound_trait_ref.def_id(), + )); + } else { + let new_trait_ref = self.erase_late_bound_regions(bound_trait_ref); + + let (xform_self_ty, xform_ret_ty) = + self.xform_self_ty(item, new_trait_ref.self_ty(), new_trait_ref.substs); + self.push_candidate( + Candidate { + xform_self_ty, + xform_ret_ty, + item, + import_ids: import_ids.clone(), + kind: TraitCandidate(new_trait_ref), + }, + false, + ); + } + } + } } else { debug_assert!(self.tcx.is_trait(trait_def_id)); if self.tcx.trait_is_auto(trait_def_id) { @@ -987,7 +994,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } let (xform_self_ty, xform_ret_ty) = - self.xform_self_ty(&item, trait_ref.self_ty(), trait_substs); + self.xform_self_ty(item, trait_ref.self_ty(), trait_substs); self.push_candidate( Candidate { xform_self_ty, @@ -1014,7 +1021,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { .filter(|candidate| candidate_filter(&candidate.item)) .filter(|candidate| { if let Some(return_ty) = self.return_type { - self.matches_return_type(&candidate.item, None, return_ty) + self.matches_return_type(candidate.item, None, return_ty) } else { true } @@ -1076,29 +1083,20 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { if let Some((kind, def_id)) = private_candidate { return Err(MethodError::PrivateMatch(kind, def_id, out_of_scope_traits)); } - let lev_candidate = self.probe_for_lev_candidate()?; + let similar_candidate = self.probe_for_similar_candidate()?; Err(MethodError::NoMatch(NoMatchData { static_candidates, unsatisfied_predicates, out_of_scope_traits, - lev_candidate, + similar_candidate, mode: self.mode, })) } fn pick_core(&self) -> Option<PickResult<'tcx>> { - let pick = self.pick_all_method(Some(&mut vec![])); - - // In this case unstable picking is done by `pick_method`. - if !self.tcx.sess.opts.unstable_opts.pick_stable_methods_before_any_unstable { - return pick; - } - - if pick.is_none() { - return self.pick_all_method(None); - } - pick + // Pick stable methods only first, and consider unstable candidates if not found. + self.pick_all_method(Some(&mut vec![])).or_else(|| self.pick_all_method(None)) } fn pick_all_method( @@ -1237,54 +1235,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { }) } - fn pick_method_with_unstable(&self, self_ty: Ty<'tcx>) -> Option<PickResult<'tcx>> { - debug!("pick_method_with_unstable(self_ty={})", self.ty_to_string(self_ty)); - - let mut possibly_unsatisfied_predicates = Vec::new(); - - for (kind, candidates) in - &[("inherent", &self.inherent_candidates), ("extension", &self.extension_candidates)] - { - debug!("searching {} candidates", kind); - let res = self.consider_candidates( - self_ty, - candidates, - &mut possibly_unsatisfied_predicates, - Some(&mut vec![]), - ); - if res.is_some() { - return res; - } - } - - for (kind, candidates) in - &[("inherent", &self.inherent_candidates), ("extension", &self.extension_candidates)] - { - debug!("searching unstable {kind} candidates"); - let res = self.consider_candidates( - self_ty, - candidates, - &mut possibly_unsatisfied_predicates, - None, - ); - if res.is_some() { - return res; - } - } - - self.unsatisfied_predicates.borrow_mut().extend(possibly_unsatisfied_predicates); - None - } - fn pick_method( &self, self_ty: Ty<'tcx>, mut unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, ) -> Option<PickResult<'tcx>> { - if !self.tcx.sess.opts.unstable_opts.pick_stable_methods_before_any_unstable { - return self.pick_method_with_unstable(self_ty); - } - debug!("pick_method(self_ty={})", self.ty_to_string(self_ty)); let mut possibly_unsatisfied_predicates = Vec::new(); @@ -1358,13 +1313,12 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { return Some(Err(MethodError::Ambiguity(sources))); } - applicable_candidates.pop().map(|(probe, status)| { - if status == ProbeResult::Match { + applicable_candidates.pop().map(|(probe, status)| match status { + ProbeResult::Match => { Ok(probe .to_unadjusted_pick(self_ty, unstable_candidates.cloned().unwrap_or_default())) - } else { - Err(MethodError::BadReturnType) } + ProbeResult::NoMatch | ProbeResult::BadReturnType => Err(MethodError::BadReturnType), }) } } @@ -1412,8 +1366,8 @@ impl<'tcx> Pick<'tcx> { span, format!( "{} {} with this name may be added to the standard library in the future", - def_kind.article(), - def_kind.descr(self.item.def_id), + tcx.def_kind_descr_article(def_kind, self.item.def_id), + tcx.def_kind_descr(def_kind, self.item.def_id), ), |lint| { match (self.item.kind, self.item.container) { @@ -1482,7 +1436,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { TraitCandidate(trait_ref) => self.probe(|_| { let _ = self .at(&ObligationCause::dummy(), self.param_env) - .define_opaque_types(false) .sup(candidate.xform_self_ty, self_ty); match self.select_trait_candidate(trait_ref) { Ok(Some(traits::ImplSource::UserDefined(ref impl_data))) => { @@ -1512,7 +1465,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { // First check that the self type can be related. let sub_obligations = match self .at(&ObligationCause::dummy(), self.param_env) - .define_opaque_types(false) .sup(probe.xform_self_ty, self_ty) { Ok(InferOk { obligations, value: () }) => obligations, @@ -1567,7 +1519,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { traits::ImplDerivedObligation(Box::new( traits::ImplDerivedObligationCause { derived, - impl_def_id, + impl_or_alias_def_id: impl_def_id, + impl_def_predicate_index: None, span, }, )) @@ -1728,7 +1681,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { if let ProbeResult::Match = result && self .at(&ObligationCause::dummy(), self.param_env) - .define_opaque_types(false) .sup(return_ty, xform_ret_ty) .is_err() { @@ -1786,8 +1738,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { /// Similarly to `probe_for_return_type`, this method attempts to find the best matching /// candidate method where the method name may have been misspelled. Similarly to other - /// Levenshtein based suggestions, we provide at most one such suggestion. - fn probe_for_lev_candidate(&mut self) -> Result<Option<ty::AssocItem>, MethodError<'tcx>> { + /// edit distance based suggestions, we provide at most one such suggestion. + fn probe_for_similar_candidate(&mut self) -> Result<Option<ty::AssocItem>, MethodError<'tcx>> { debug!("probing for method names similar to {:?}", self.method_name); let steps = self.steps.clone(); @@ -1831,6 +1783,12 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { None, ) } + .or_else(|| { + applicable_close_candidates + .iter() + .find(|cand| self.matches_by_doc_alias(cand.def_id)) + .map(|cand| cand.name) + }) .unwrap(); Ok(applicable_close_candidates.into_iter().find(|method| method.name == best_name)) } @@ -1867,7 +1825,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { #[instrument(level = "debug", skip(self))] fn xform_self_ty( &self, - item: &ty::AssocItem, + item: ty::AssocItem, impl_ty: Ty<'tcx>, substs: SubstsRef<'tcx>, ) -> (Ty<'tcx>, Option<Ty<'tcx>>) { @@ -1881,7 +1839,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { #[instrument(level = "debug", skip(self))] fn xform_method_sig(&self, method: DefId, substs: SubstsRef<'tcx>) -> ty::FnSig<'tcx> { - let fn_sig = self.tcx.bound_fn_sig(method); + let fn_sig = self.tcx.fn_sig(method); debug!(?fn_sig); assert!(!substs.has_escaping_bound_vars()); @@ -1924,27 +1882,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { &self, impl_def_id: DefId, ) -> (ty::EarlyBinder<Ty<'tcx>>, SubstsRef<'tcx>) { - (self.tcx.bound_type_of(impl_def_id), self.fresh_item_substs(impl_def_id)) - } - - fn fresh_item_substs(&self, def_id: DefId) -> SubstsRef<'tcx> { - InternalSubsts::for_item(self.tcx, def_id, |param, _| match param.kind { - GenericParamDefKind::Lifetime => self.tcx.lifetimes.re_erased.into(), - GenericParamDefKind::Type { .. } => self - .next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::SubstitutionPlaceholder, - span: self.tcx.def_span(def_id), - }) - .into(), - GenericParamDefKind::Const { .. } => { - let span = self.tcx.def_span(def_id); - let origin = ConstVariableOrigin { - kind: ConstVariableOriginKind::SubstitutionPlaceholder, - span, - }; - self.next_const_var(self.tcx.type_of(param.def_id), origin).into() - } - }) + (self.tcx.type_of(impl_def_id), self.fresh_item_substs(impl_def_id)) } /// Replaces late-bound-regions bound by `value` with `'static` using @@ -1967,7 +1905,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { /// so forth. fn erase_late_bound_regions<T>(&self, value: ty::Binder<'tcx, T>) -> T where - T: TypeFoldable<'tcx>, + T: TypeFoldable<TyCtxt<'tcx>>, { self.tcx.erase_late_bound_regions(value) } @@ -1981,6 +1919,38 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } + /// Determine if the associated item withe the given DefId matches + /// the desired name via a doc alias. + fn matches_by_doc_alias(&self, def_id: DefId) -> bool { + let Some(name) = self.method_name else { return false; }; + let Some(local_def_id) = def_id.as_local() else { return false; }; + let hir_id = self.fcx.tcx.hir().local_def_id_to_hir_id(local_def_id); + let attrs = self.fcx.tcx.hir().attrs(hir_id); + for attr in attrs { + let sym::doc = attr.name_or_empty() else { continue; }; + let Some(values) = attr.meta_item_list() else { continue; }; + for v in values { + if v.name_or_empty() != sym::alias { + continue; + } + if let Some(nested) = v.meta_item_list() { + // #[doc(alias("foo", "bar"))] + for n in nested { + if let Some(lit) = n.lit() && name.as_str() == lit.symbol.as_str() { + return true; + } + } + } else if let Some(meta) = v.meta_item() + && let Some(lit) = meta.name_value_literal() + && name.as_str() == lit.symbol.as_str() { + // #[doc(alias = "foo")] + return true; + } + } + } + false + } + /// Finds the method with the appropriate name (or return type, as the case may be). If /// `allow_similar_names` is set, find methods with close-matching names. // The length of the returned iterator is nearly always 0 or 1 and this @@ -1996,8 +1966,14 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { if !self.is_relevant_kind_for_mode(x.kind) { return false; } - match lev_distance_with_substrings(name.as_str(), x.name.as_str(), max_dist) - { + if self.matches_by_doc_alias(x.def_id) { + return true; + } + match edit_distance_with_substrings( + name.as_str(), + x.name.as_str(), + max_dist, + ) { Some(d) => d > 0, None => false, } |