diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
commit | 698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch) | |
tree | 173a775858bd501c378080a10dca74132f05bc50 /compiler/rustc_typeck/src/check/method | |
parent | Initial commit. (diff) | |
download | rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip |
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | compiler/rustc_typeck/src/check/method/confirm.rs | 582 | ||||
-rw-r--r-- | compiler/rustc_typeck/src/check/method/mod.rs | 658 | ||||
-rw-r--r-- | compiler/rustc_typeck/src/check/method/prelude2021.rs | 419 | ||||
-rw-r--r-- | compiler/rustc_typeck/src/check/method/probe.rs | 1932 | ||||
-rw-r--r-- | compiler/rustc_typeck/src/check/method/suggest.rs | 2286 |
5 files changed, 5877 insertions, 0 deletions
diff --git a/compiler/rustc_typeck/src/check/method/confirm.rs b/compiler/rustc_typeck/src/check/method/confirm.rs new file mode 100644 index 000000000..2c89b63ae --- /dev/null +++ b/compiler/rustc_typeck/src/check/method/confirm.rs @@ -0,0 +1,582 @@ +use super::{probe, MethodCallee}; + +use crate::astconv::{AstConv, CreateSubstsForGenericArgsCtxt, IsMethodCall}; +use crate::check::{callee, FnCtxt}; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_hir::GenericArg; +use rustc_infer::infer::{self, InferOk}; +use rustc_middle::traits::{ObligationCauseCode, UnifyReceiverContext}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCast}; +use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutability}; +use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::ty::subst::{self, Subst, SubstsRef}; +use rustc_middle::ty::{self, GenericParamDefKind, Ty}; +use rustc_span::Span; +use rustc_trait_selection::traits; + +use std::iter; +use std::ops::Deref; + +struct ConfirmContext<'a, 'tcx> { + fcx: &'a FnCtxt<'a, 'tcx>, + span: Span, + self_expr: &'tcx hir::Expr<'tcx>, + call_expr: &'tcx hir::Expr<'tcx>, +} + +impl<'a, 'tcx> Deref for ConfirmContext<'a, 'tcx> { + type Target = FnCtxt<'a, 'tcx>; + fn deref(&self) -> &Self::Target { + self.fcx + } +} + +#[derive(Debug)] +pub struct ConfirmResult<'tcx> { + pub callee: MethodCallee<'tcx>, + pub illegal_sized_bound: Option<Span>, +} + +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + pub fn confirm_method( + &self, + span: Span, + self_expr: &'tcx hir::Expr<'tcx>, + call_expr: &'tcx hir::Expr<'tcx>, + unadjusted_self_ty: Ty<'tcx>, + pick: probe::Pick<'tcx>, + segment: &hir::PathSegment<'_>, + ) -> ConfirmResult<'tcx> { + debug!( + "confirm(unadjusted_self_ty={:?}, pick={:?}, generic_args={:?})", + unadjusted_self_ty, pick, segment.args, + ); + + let mut confirm_cx = ConfirmContext::new(self, span, self_expr, call_expr); + confirm_cx.confirm(unadjusted_self_ty, pick, segment) + } +} + +impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { + fn new( + fcx: &'a FnCtxt<'a, 'tcx>, + span: Span, + self_expr: &'tcx hir::Expr<'tcx>, + call_expr: &'tcx hir::Expr<'tcx>, + ) -> ConfirmContext<'a, 'tcx> { + ConfirmContext { fcx, span, self_expr, call_expr } + } + + fn confirm( + &mut self, + unadjusted_self_ty: Ty<'tcx>, + pick: probe::Pick<'tcx>, + segment: &hir::PathSegment<'_>, + ) -> ConfirmResult<'tcx> { + // Adjust the self expression the user provided and obtain the adjusted type. + let self_ty = self.adjust_self_ty(unadjusted_self_ty, &pick); + + // Create substitutions for the method's type parameters. + let rcvr_substs = self.fresh_receiver_substs(self_ty, &pick); + let all_substs = self.instantiate_method_substs(&pick, segment, rcvr_substs); + + debug!("rcvr_substs={rcvr_substs:?}, all_substs={all_substs:?}"); + + // Create the final signature for the method, replacing late-bound regions. + let (method_sig, method_predicates) = self.instantiate_method_sig(&pick, all_substs); + + // If there is a `Self: Sized` bound and `Self` is a trait object, it is possible that + // something which derefs to `Self` actually implements the trait and the caller + // wanted to make a static dispatch on it but forgot to import the trait. + // See test `src/test/ui/issue-35976.rs`. + // + // In that case, we'll error anyway, but we'll also re-run the search with all traits + // in scope, and if we find another method which can be used, we'll output an + // appropriate hint suggesting to import the trait. + let filler_substs = rcvr_substs + .extend_to(self.tcx, pick.item.def_id, |def, _| self.tcx.mk_param_from_def(def)); + let illegal_sized_bound = self.predicates_require_illegal_sized_bound( + &self.tcx.predicates_of(pick.item.def_id).instantiate(self.tcx, filler_substs), + ); + + // Unify the (adjusted) self type with what the method expects. + // + // SUBTLE: if we want good error messages, because of "guessing" while matching + // traits, no trait system method can be called before this point because they + // could alter our Self-type, except for normalizing the receiver from the + // signature (which is also done during probing). + let method_sig_rcvr = self.normalize_associated_types_in(self.span, method_sig.inputs()[0]); + debug!( + "confirm: self_ty={:?} method_sig_rcvr={:?} method_sig={:?} method_predicates={:?}", + self_ty, method_sig_rcvr, method_sig, method_predicates + ); + self.unify_receivers(self_ty, method_sig_rcvr, &pick, all_substs); + + let (method_sig, method_predicates) = + self.normalize_associated_types_in(self.span, (method_sig, method_predicates)); + let method_sig = ty::Binder::dummy(method_sig); + + // Make sure nobody calls `drop()` explicitly. + self.enforce_illegal_method_limitations(&pick); + + // Add any trait/regions obligations specified on the method's type parameters. + // We won't add these if we encountered an illegal sized bound, so that we can use + // a custom error in that case. + if illegal_sized_bound.is_none() { + self.add_obligations( + self.tcx.mk_fn_ptr(method_sig), + all_substs, + method_predicates, + pick.item.def_id, + ); + } + + // Create the final `MethodCallee`. + let callee = MethodCallee { + def_id: pick.item.def_id, + substs: all_substs, + sig: method_sig.skip_binder(), + }; + ConfirmResult { callee, illegal_sized_bound } + } + + /////////////////////////////////////////////////////////////////////////// + // ADJUSTMENTS + + fn adjust_self_ty( + &mut self, + unadjusted_self_ty: Ty<'tcx>, + pick: &probe::Pick<'tcx>, + ) -> Ty<'tcx> { + // Commit the autoderefs by calling `autoderef` again, but this + // time writing the results into the various typeck results. + let mut autoderef = + self.autoderef_overloaded_span(self.span, unadjusted_self_ty, self.call_expr.span); + let Some((ty, n)) = autoderef.nth(pick.autoderefs) else { + return self.tcx.ty_error_with_message( + rustc_span::DUMMY_SP, + &format!("failed autoderef {}", pick.autoderefs), + ); + }; + assert_eq!(n, pick.autoderefs); + + let mut adjustments = self.adjust_steps(&autoderef); + let mut target = self.structurally_resolved_type(autoderef.span(), ty); + + match pick.autoref_or_ptr_adjustment { + Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl, unsize }) => { + let region = self.next_region_var(infer::Autoref(self.span)); + // Type we're wrapping in a reference, used later for unsizing + let base_ty = target; + + target = self.tcx.mk_ref(region, ty::TypeAndMut { mutbl, ty: target }); + let mutbl = match mutbl { + hir::Mutability::Not => AutoBorrowMutability::Not, + hir::Mutability::Mut => AutoBorrowMutability::Mut { + // Method call receivers are the primary use case + // for two-phase borrows. + allow_two_phase_borrow: AllowTwoPhase::Yes, + }, + }; + adjustments.push(Adjustment { + kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)), + target, + }); + + if unsize { + let unsized_ty = if let ty::Array(elem_ty, _) = base_ty.kind() { + self.tcx.mk_slice(*elem_ty) + } else { + bug!( + "AutorefOrPtrAdjustment's unsize flag should only be set for array ty, found {}", + base_ty + ) + }; + target = self + .tcx + .mk_ref(region, ty::TypeAndMut { mutbl: mutbl.into(), ty: unsized_ty }); + adjustments + .push(Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), target }); + } + } + Some(probe::AutorefOrPtrAdjustment::ToConstPtr) => { + target = match target.kind() { + &ty::RawPtr(ty::TypeAndMut { ty, mutbl }) => { + assert_eq!(mutbl, hir::Mutability::Mut); + self.tcx.mk_ptr(ty::TypeAndMut { mutbl: hir::Mutability::Not, ty }) + } + other => panic!("Cannot adjust receiver type {:?} to const ptr", other), + }; + + adjustments.push(Adjustment { + kind: Adjust::Pointer(PointerCast::MutToConstPointer), + target, + }); + } + None => {} + } + + self.register_predicates(autoderef.into_obligations()); + + // Write out the final adjustments. + self.apply_adjustments(self.self_expr, adjustments); + + target + } + + /// Returns a set of substitutions for the method *receiver* where all type and region + /// parameters are instantiated with fresh variables. This substitution does not include any + /// parameters declared on the method itself. + /// + /// Note that this substitution may include late-bound regions from the impl level. If so, + /// these are instantiated later in the `instantiate_method_sig` routine. + fn fresh_receiver_substs( + &mut self, + self_ty: Ty<'tcx>, + pick: &probe::Pick<'tcx>, + ) -> SubstsRef<'tcx> { + match pick.kind { + probe::InherentImplPick => { + let impl_def_id = pick.item.container_id(self.tcx); + assert!( + self.tcx.impl_trait_ref(impl_def_id).is_none(), + "impl {:?} is not an inherent impl", + impl_def_id + ); + self.fresh_substs_for_item(self.span, impl_def_id) + } + + probe::ObjectPick => { + let trait_def_id = pick.item.container_id(self.tcx); + self.extract_existential_trait_ref(self_ty, |this, object_ty, principal| { + // The object data has no entry for the Self + // Type. For the purposes of this method call, we + // substitute the object type itself. This + // wouldn't be a sound substitution in all cases, + // since each instance of the object type is a + // different existential and hence could match + // distinct types (e.g., if `Self` appeared as an + // argument type), but those cases have already + // been ruled out when we deemed the trait to be + // "object safe". + let original_poly_trait_ref = principal.with_self_ty(this.tcx, object_ty); + let upcast_poly_trait_ref = this.upcast(original_poly_trait_ref, trait_def_id); + let upcast_trait_ref = + this.replace_bound_vars_with_fresh_vars(upcast_poly_trait_ref); + debug!( + "original_poly_trait_ref={:?} upcast_trait_ref={:?} target_trait={:?}", + original_poly_trait_ref, upcast_trait_ref, trait_def_id + ); + upcast_trait_ref.substs + }) + } + + probe::TraitPick => { + let trait_def_id = pick.item.container_id(self.tcx); + + // Make a trait reference `$0 : Trait<$1...$n>` + // consisting entirely of type variables. Later on in + // the process we will unify the transformed-self-type + // of the method with the actual type in order to + // unify some of these variables. + self.fresh_substs_for_item(self.span, trait_def_id) + } + + probe::WhereClausePick(poly_trait_ref) => { + // Where clauses can have bound regions in them. We need to instantiate + // those to convert from a poly-trait-ref to a trait-ref. + self.replace_bound_vars_with_fresh_vars(poly_trait_ref).substs + } + } + } + + fn extract_existential_trait_ref<R, F>(&mut self, self_ty: Ty<'tcx>, mut closure: F) -> R + where + F: FnMut(&mut ConfirmContext<'a, 'tcx>, Ty<'tcx>, ty::PolyExistentialTraitRef<'tcx>) -> R, + { + // If we specified that this is an object method, then the + // self-type ought to be something that can be dereferenced to + // yield an object-type (e.g., `&Object` or `Box<Object>` + // etc). + + // FIXME: this feels, like, super dubious + self.fcx + .autoderef(self.span, self_ty) + .include_raw_pointers() + .find_map(|(ty, _)| match ty.kind() { + ty::Dynamic(data, ..) => Some(closure( + self, + ty, + data.principal().unwrap_or_else(|| { + span_bug!(self.span, "calling trait method on empty object?") + }), + )), + _ => None, + }) + .unwrap_or_else(|| { + span_bug!( + self.span, + "self-type `{}` for ObjectPick never dereferenced to an object", + self_ty + ) + }) + } + + fn instantiate_method_substs( + &mut self, + pick: &probe::Pick<'tcx>, + seg: &hir::PathSegment<'_>, + parent_substs: SubstsRef<'tcx>, + ) -> SubstsRef<'tcx> { + // Determine the values for the generic parameters of the method. + // If they were not explicitly supplied, just construct fresh + // variables. + let generics = self.tcx.generics_of(pick.item.def_id); + + let arg_count_correct = <dyn AstConv<'_>>::check_generic_arg_count_for_call( + self.tcx, + self.span, + pick.item.def_id, + generics, + seg, + IsMethodCall::Yes, + ); + + // Create subst for early-bound lifetime parameters, combining + // parameters from the type and those from the method. + assert_eq!(generics.parent_count, parent_substs.len()); + + struct MethodSubstsCtxt<'a, 'tcx> { + cfcx: &'a ConfirmContext<'a, 'tcx>, + pick: &'a probe::Pick<'tcx>, + seg: &'a hir::PathSegment<'a>, + } + impl<'a, 'tcx> CreateSubstsForGenericArgsCtxt<'a, 'tcx> for MethodSubstsCtxt<'a, 'tcx> { + fn args_for_def_id( + &mut self, + def_id: DefId, + ) -> (Option<&'a hir::GenericArgs<'a>>, bool) { + if def_id == self.pick.item.def_id { + if let Some(data) = self.seg.args { + return (Some(data), false); + } + } + (None, false) + } + + fn provided_kind( + &mut self, + param: &ty::GenericParamDef, + arg: &GenericArg<'_>, + ) -> subst::GenericArg<'tcx> { + match (¶m.kind, arg) { + (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => { + <dyn AstConv<'_>>::ast_region_to_region(self.cfcx.fcx, lt, Some(param)) + .into() + } + (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => { + self.cfcx.to_ty(ty).into() + } + (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => { + self.cfcx.const_arg_to_const(&ct.value, param.def_id).into() + } + (GenericParamDefKind::Type { .. }, GenericArg::Infer(inf)) => { + self.cfcx.ty_infer(Some(param), inf.span).into() + } + (GenericParamDefKind::Const { .. }, GenericArg::Infer(inf)) => { + let tcx = self.cfcx.tcx(); + self.cfcx.ct_infer(tcx.type_of(param.def_id), Some(param), inf.span).into() + } + _ => unreachable!(), + } + } + + fn inferred_kind( + &mut self, + _substs: Option<&[subst::GenericArg<'tcx>]>, + param: &ty::GenericParamDef, + _infer_args: bool, + ) -> subst::GenericArg<'tcx> { + self.cfcx.var_for_def(self.cfcx.span, param) + } + } + <dyn AstConv<'_>>::create_substs_for_generic_args( + self.tcx, + pick.item.def_id, + parent_substs, + false, + None, + &arg_count_correct, + &mut MethodSubstsCtxt { cfcx: self, pick, seg }, + ) + } + + fn unify_receivers( + &mut self, + self_ty: Ty<'tcx>, + method_self_ty: Ty<'tcx>, + pick: &probe::Pick<'tcx>, + substs: SubstsRef<'tcx>, + ) { + debug!( + "unify_receivers: self_ty={:?} method_self_ty={:?} span={:?} pick={:?}", + self_ty, method_self_ty, self.span, pick + ); + let cause = self.cause( + self.span, + ObligationCauseCode::UnifyReceiver(Box::new(UnifyReceiverContext { + assoc_item: pick.item, + param_env: self.param_env, + substs, + })), + ); + match self.at(&cause, self.param_env).sup(method_self_ty, self_ty) { + Ok(InferOk { obligations, value: () }) => { + self.register_predicates(obligations); + } + Err(_) => { + span_bug!( + self.span, + "{} was a subtype of {} but now is not?", + self_ty, + method_self_ty + ); + } + } + } + + // NOTE: this returns the *unnormalized* predicates and method sig. Because of + // inference guessing, the predicates and method signature can't be normalized + // until we unify the `Self` type. + fn instantiate_method_sig( + &mut self, + pick: &probe::Pick<'tcx>, + all_substs: SubstsRef<'tcx>, + ) -> (ty::FnSig<'tcx>, ty::InstantiatedPredicates<'tcx>) { + debug!("instantiate_method_sig(pick={:?}, all_substs={:?})", pick, all_substs); + + // Instantiate the bounds on the method with the + // type/early-bound-regions substitutions performed. There can + // be no late-bound regions appearing here. + let def_id = pick.item.def_id; + let method_predicates = self.tcx.predicates_of(def_id).instantiate(self.tcx, all_substs); + + debug!("method_predicates after subst = {:?}", method_predicates); + + let sig = self.tcx.bound_fn_sig(def_id); + + let sig = sig.subst(self.tcx, all_substs); + debug!("type scheme substituted, sig={:?}", sig); + + let sig = self.replace_bound_vars_with_fresh_vars(sig); + debug!("late-bound lifetimes from method instantiated, sig={:?}", sig); + + (sig, method_predicates) + } + + fn add_obligations( + &mut self, + fty: Ty<'tcx>, + all_substs: SubstsRef<'tcx>, + method_predicates: ty::InstantiatedPredicates<'tcx>, + def_id: DefId, + ) { + debug!( + "add_obligations: fty={:?} all_substs={:?} method_predicates={:?} def_id={:?}", + fty, all_substs, method_predicates, def_id + ); + + // FIXME: could replace with the following, but we already calculated `method_predicates`, + // so we just call `predicates_for_generics` directly to avoid redoing work. + // `self.add_required_obligations(self.span, def_id, &all_substs);` + for obligation in traits::predicates_for_generics( + traits::ObligationCause::new(self.span, self.body_id, traits::ItemObligation(def_id)), + self.param_env, + method_predicates, + ) { + self.register_predicate(obligation); + } + + // this is a projection from a trait reference, so we have to + // make sure that the trait reference inputs are well-formed. + self.add_wf_bounds(all_substs, self.call_expr); + + // the function type must also be well-formed (this is not + // implied by the substs being well-formed because of inherent + // impls and late-bound regions - see issue #28609). + self.register_wf_obligation(fty.into(), self.span, traits::WellFormed(None)); + } + + /////////////////////////////////////////////////////////////////////////// + // MISCELLANY + + fn predicates_require_illegal_sized_bound( + &self, + predicates: &ty::InstantiatedPredicates<'tcx>, + ) -> Option<Span> { + let sized_def_id = self.tcx.lang_items().sized_trait()?; + + traits::elaborate_predicates(self.tcx, predicates.predicates.iter().copied()) + // We don't care about regions here. + .filter_map(|obligation| match obligation.predicate.kind().skip_binder() { + ty::PredicateKind::Trait(trait_pred) if trait_pred.def_id() == sized_def_id => { + let span = iter::zip(&predicates.predicates, &predicates.spans) + .find_map( + |(p, span)| { + if *p == obligation.predicate { Some(*span) } else { None } + }, + ) + .unwrap_or(rustc_span::DUMMY_SP); + Some((trait_pred, span)) + } + _ => None, + }) + .find_map(|(trait_pred, span)| match trait_pred.self_ty().kind() { + ty::Dynamic(..) => Some(span), + _ => None, + }) + } + + fn enforce_illegal_method_limitations(&self, pick: &probe::Pick<'_>) { + // Disallow calls to the method `drop` defined in the `Drop` trait. + if let Some(trait_def_id) = pick.item.trait_container(self.tcx) { + callee::check_legal_trait_for_method_call( + self.tcx, + self.span, + Some(self.self_expr.span), + self.call_expr.span, + trait_def_id, + ) + } + } + + fn upcast( + &mut self, + source_trait_ref: ty::PolyTraitRef<'tcx>, + target_trait_def_id: DefId, + ) -> ty::PolyTraitRef<'tcx> { + let upcast_trait_refs = + traits::upcast_choices(self.tcx, source_trait_ref, target_trait_def_id); + + // must be exactly one trait ref or we'd get an ambig error etc + if upcast_trait_refs.len() != 1 { + span_bug!( + self.span, + "cannot uniquely upcast `{:?}` to `{:?}`: `{:?}`", + source_trait_ref, + target_trait_def_id, + upcast_trait_refs + ); + } + + upcast_trait_refs.into_iter().next().unwrap() + } + + fn replace_bound_vars_with_fresh_vars<T>(&self, value: ty::Binder<'tcx, T>) -> T + where + T: TypeFoldable<'tcx> + Copy, + { + self.fcx.replace_bound_vars_with_fresh_vars(self.span, infer::FnCall, value) + } +} diff --git a/compiler/rustc_typeck/src/check/method/mod.rs b/compiler/rustc_typeck/src/check/method/mod.rs new file mode 100644 index 000000000..0e678c41f --- /dev/null +++ b/compiler/rustc_typeck/src/check/method/mod.rs @@ -0,0 +1,658 @@ +//! Method lookup: the secret sauce of Rust. See the [rustc dev guide] for more information. +//! +//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/method-lookup.html + +mod confirm; +mod prelude2021; +pub mod probe; +mod suggest; + +pub use self::suggest::SelfSource; +pub use self::MethodError::*; + +use crate::check::{Expectation, FnCtxt}; +use crate::ObligationCause; +use rustc_data_structures::sync::Lrc; +use rustc_errors::{Applicability, Diagnostic}; +use rustc_hir as hir; +use rustc_hir::def::{CtorOf, DefKind, Namespace}; +use rustc_hir::def_id::DefId; +use rustc_infer::infer::{self, InferOk}; +use rustc_middle::ty::subst::Subst; +use rustc_middle::ty::subst::{InternalSubsts, SubstsRef}; +use rustc_middle::ty::{ + self, AssocKind, DefIdTree, GenericParamDefKind, ProjectionPredicate, ProjectionTy, Term, + ToPredicate, Ty, TypeVisitable, +}; +use rustc_span::symbol::Ident; +use rustc_span::Span; +use rustc_trait_selection::traits; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; + +use self::probe::{IsSuggestion, ProbeScope}; + +pub fn provide(providers: &mut ty::query::Providers) { + probe::provide(providers); +} + +#[derive(Clone, Copy, Debug)] +pub struct MethodCallee<'tcx> { + /// Impl method ID, for inherent methods, or trait method ID, otherwise. + pub def_id: DefId, + pub substs: SubstsRef<'tcx>, + + /// Instantiated method signature, i.e., it has been + /// substituted, normalized, and has had late-bound + /// lifetimes replaced with inference variables. + pub sig: ty::FnSig<'tcx>, +} + +#[derive(Debug)] +pub enum MethodError<'tcx> { + // Did not find an applicable method, but we did find various near-misses that may work. + NoMatch(NoMatchData<'tcx>), + + // Multiple methods might apply. + Ambiguity(Vec<CandidateSource>), + + // Found an applicable method, but it is not visible. The third argument contains a list of + // not-in-scope traits which may work. + PrivateMatch(DefKind, DefId, Vec<DefId>), + + // Found a `Self: Sized` bound where `Self` is a trait object, also the caller may have + // forgotten to import a trait. + IllegalSizedBound(Vec<DefId>, bool, Span), + + // Found a match, but the return type is wrong + BadReturnType, +} + +// Contains a list of static methods that may apply, a list of unsatisfied trait predicates which +// could lead to matches if satisfied, and a list of not-in-scope traits which may work. +#[derive(Debug)] +pub struct NoMatchData<'tcx> { + pub static_candidates: Vec<CandidateSource>, + pub unsatisfied_predicates: + Vec<(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>, Option<ObligationCause<'tcx>>)>, + pub out_of_scope_traits: Vec<DefId>, + pub lev_candidate: Option<ty::AssocItem>, + pub mode: probe::Mode, +} + +// A pared down enum describing just the places from which a method +// candidate can arise. Used for error reporting only. +#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub enum CandidateSource { + Impl(DefId), + Trait(DefId /* trait id */), +} + +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + /// Determines whether the type `self_ty` supports a method name `method_name` or not. + #[instrument(level = "debug", skip(self))] + pub fn method_exists( + &self, + method_name: Ident, + self_ty: Ty<'tcx>, + call_expr_id: hir::HirId, + allow_private: bool, + ) -> bool { + let mode = probe::Mode::MethodCall; + match self.probe_for_name( + method_name.span, + mode, + method_name, + IsSuggestion(false), + self_ty, + call_expr_id, + ProbeScope::TraitsInScope, + ) { + Ok(..) => true, + Err(NoMatch(..)) => false, + Err(Ambiguity(..)) => true, + Err(PrivateMatch(..)) => allow_private, + Err(IllegalSizedBound(..)) => true, + Err(BadReturnType) => bug!("no return type expectations but got BadReturnType"), + } + } + + /// Adds a suggestion to call the given method to the provided diagnostic. + #[instrument(level = "debug", skip(self, err, call_expr))] + pub(crate) fn suggest_method_call( + &self, + err: &mut Diagnostic, + msg: &str, + method_name: Ident, + self_ty: Ty<'tcx>, + call_expr: &hir::Expr<'_>, + span: Option<Span>, + ) { + let params = self + .probe_for_name( + method_name.span, + probe::Mode::MethodCall, + method_name, + IsSuggestion(false), + self_ty, + call_expr.hir_id, + ProbeScope::TraitsInScope, + ) + .map(|pick| { + let sig = self.tcx.fn_sig(pick.item.def_id); + sig.inputs().skip_binder().len().saturating_sub(1) + }) + .unwrap_or(0); + + // Account for `foo.bar<T>`; + let sugg_span = span.unwrap_or(call_expr.span).shrink_to_hi(); + let (suggestion, applicability) = ( + format!("({})", (0..params).map(|_| "_").collect::<Vec<_>>().join(", ")), + if params > 0 { Applicability::HasPlaceholders } else { Applicability::MaybeIncorrect }, + ); + + err.span_suggestion_verbose(sugg_span, msg, suggestion, applicability); + } + + /// Performs method lookup. If lookup is successful, it will return the callee + /// and store an appropriate adjustment for the self-expr. In some cases it may + /// report an error (e.g., invoking the `drop` method). + /// + /// # Arguments + /// + /// Given a method call like `foo.bar::<T1,...Tn>(a, b + 1, ...)`: + /// + /// * `self`: the surrounding `FnCtxt` (!) + /// * `self_ty`: the (unadjusted) type of the self expression (`foo`) + /// * `segment`: the name and generic arguments of the method (`bar::<T1, ...Tn>`) + /// * `span`: the span for the method call + /// * `call_expr`: the complete method call: (`foo.bar::<T1,...Tn>(...)`) + /// * `self_expr`: the self expression (`foo`) + /// * `args`: the expressions of the arguments (`a, b + 1, ...`) + #[instrument(level = "debug", skip(self, call_expr, self_expr))] + pub fn lookup_method( + &self, + self_ty: Ty<'tcx>, + segment: &hir::PathSegment<'_>, + span: Span, + call_expr: &'tcx hir::Expr<'tcx>, + self_expr: &'tcx hir::Expr<'tcx>, + args: &'tcx [hir::Expr<'tcx>], + ) -> Result<MethodCallee<'tcx>, MethodError<'tcx>> { + debug!( + "lookup(method_name={}, self_ty={:?}, call_expr={:?}, self_expr={:?})", + segment.ident, self_ty, call_expr, self_expr + ); + + let pick = + self.lookup_probe(span, segment.ident, self_ty, call_expr, ProbeScope::TraitsInScope)?; + + self.lint_dot_call_from_2018(self_ty, segment, span, call_expr, self_expr, &pick, args); + + for import_id in &pick.import_ids { + debug!("used_trait_import: {:?}", import_id); + Lrc::get_mut(&mut self.typeck_results.borrow_mut().used_trait_imports) + .unwrap() + .insert(*import_id); + } + + self.tcx.check_stability(pick.item.def_id, Some(call_expr.hir_id), span, None); + + let result = + self.confirm_method(span, self_expr, call_expr, self_ty, pick.clone(), segment); + debug!("result = {:?}", result); + + if let Some(span) = result.illegal_sized_bound { + let mut needs_mut = false; + if let ty::Ref(region, t_type, mutability) = self_ty.kind() { + let trait_type = self + .tcx + .mk_ref(*region, ty::TypeAndMut { ty: *t_type, mutbl: mutability.invert() }); + // We probe again to see if there might be a borrow mutability discrepancy. + match self.lookup_probe( + span, + segment.ident, + trait_type, + call_expr, + ProbeScope::TraitsInScope, + ) { + Ok(ref new_pick) if *new_pick != pick => { + needs_mut = true; + } + _ => {} + } + } + + // We probe again, taking all traits into account (not only those in scope). + let mut candidates = match self.lookup_probe( + span, + segment.ident, + self_ty, + call_expr, + ProbeScope::AllTraits, + ) { + // If we find a different result the caller probably forgot to import a trait. + Ok(ref new_pick) if *new_pick != pick => vec![new_pick.item.container_id(self.tcx)], + Err(Ambiguity(ref sources)) => sources + .iter() + .filter_map(|source| { + match *source { + // Note: this cannot come from an inherent impl, + // because the first probing succeeded. + CandidateSource::Impl(def) => self.tcx.trait_id_of_impl(def), + CandidateSource::Trait(_) => None, + } + }) + .collect(), + _ => Vec::new(), + }; + candidates.retain(|candidate| *candidate != self.tcx.parent(result.callee.def_id)); + + return Err(IllegalSizedBound(candidates, needs_mut, span)); + } + + Ok(result.callee) + } + + #[instrument(level = "debug", skip(self, call_expr))] + pub fn lookup_probe( + &self, + span: Span, + method_name: Ident, + self_ty: Ty<'tcx>, + call_expr: &'tcx hir::Expr<'tcx>, + scope: ProbeScope, + ) -> probe::PickResult<'tcx> { + let mode = probe::Mode::MethodCall; + let self_ty = self.resolve_vars_if_possible(self_ty); + self.probe_for_name( + span, + mode, + method_name, + IsSuggestion(false), + self_ty, + call_expr.hir_id, + scope, + ) + } + + pub(super) fn obligation_for_method( + &self, + span: Span, + trait_def_id: DefId, + self_ty: Ty<'tcx>, + opt_input_types: Option<&[Ty<'tcx>]>, + ) -> (traits::Obligation<'tcx, ty::Predicate<'tcx>>, &'tcx ty::List<ty::subst::GenericArg<'tcx>>) + { + // Construct a trait-reference `self_ty : Trait<input_tys>` + let substs = InternalSubsts::for_item(self.tcx, trait_def_id, |param, _| { + match param.kind { + GenericParamDefKind::Lifetime | GenericParamDefKind::Const { .. } => {} + GenericParamDefKind::Type { .. } => { + if param.index == 0 { + return self_ty.into(); + } else if let Some(input_types) = opt_input_types { + return input_types[param.index as usize - 1].into(); + } + } + } + self.var_for_def(span, param) + }); + + let trait_ref = ty::TraitRef::new(trait_def_id, substs); + + // Construct an obligation + let poly_trait_ref = ty::Binder::dummy(trait_ref); + ( + traits::Obligation::misc( + span, + self.body_id, + self.param_env, + poly_trait_ref.without_const().to_predicate(self.tcx), + ), + substs, + ) + } + + pub(super) fn obligation_for_op_method( + &self, + span: Span, + trait_def_id: DefId, + self_ty: Ty<'tcx>, + opt_input_type: Option<Ty<'tcx>>, + opt_input_expr: Option<&'tcx hir::Expr<'tcx>>, + expected: Expectation<'tcx>, + ) -> (traits::Obligation<'tcx, ty::Predicate<'tcx>>, &'tcx ty::List<ty::subst::GenericArg<'tcx>>) + { + // Construct a trait-reference `self_ty : Trait<input_tys>` + let substs = InternalSubsts::for_item(self.tcx, trait_def_id, |param, _| { + match param.kind { + GenericParamDefKind::Lifetime | GenericParamDefKind::Const { .. } => {} + GenericParamDefKind::Type { .. } => { + if param.index == 0 { + return self_ty.into(); + } else if let Some(input_type) = opt_input_type { + return input_type.into(); + } + } + } + self.var_for_def(span, param) + }); + + let trait_ref = ty::TraitRef::new(trait_def_id, substs); + + // Construct an obligation + let poly_trait_ref = ty::Binder::dummy(trait_ref); + let opt_output_ty = + expected.only_has_type(self).and_then(|ty| (!ty.needs_infer()).then(|| ty)); + let opt_output_assoc_item = self.tcx.associated_items(trait_def_id).find_by_name_and_kind( + self.tcx, + Ident::from_str("Output"), + AssocKind::Type, + trait_def_id, + ); + let output_pred = + opt_output_ty.zip(opt_output_assoc_item).map(|(output_ty, output_assoc_item)| { + ty::Binder::dummy(ty::PredicateKind::Projection(ProjectionPredicate { + projection_ty: ProjectionTy { substs, item_def_id: output_assoc_item.def_id }, + term: Term::Ty(output_ty), + })) + .to_predicate(self.tcx) + }); + + ( + traits::Obligation::new( + traits::ObligationCause::new( + span, + self.body_id, + traits::BinOp { + rhs_span: opt_input_expr.map(|expr| expr.span), + is_lit: opt_input_expr + .map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))), + output_pred, + }, + ), + self.param_env, + poly_trait_ref.without_const().to_predicate(self.tcx), + ), + substs, + ) + } + + /// `lookup_method_in_trait` is used for overloaded operators. + /// It does a very narrow slice of what the normal probe/confirm path does. + /// In particular, it doesn't really do any probing: it simply constructs + /// an obligation for a particular trait with the given self type and checks + /// whether that trait is implemented. + #[instrument(level = "debug", skip(self, span, opt_input_types))] + pub(super) fn lookup_method_in_trait( + &self, + span: Span, + m_name: Ident, + trait_def_id: DefId, + self_ty: Ty<'tcx>, + opt_input_types: Option<&[Ty<'tcx>]>, + ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> { + debug!( + "lookup_in_trait_adjusted(self_ty={:?}, m_name={}, trait_def_id={:?}, opt_input_types={:?})", + self_ty, m_name, trait_def_id, opt_input_types + ); + + let (obligation, substs) = + self.obligation_for_method(span, trait_def_id, self_ty, opt_input_types); + self.construct_obligation_for_trait( + span, + m_name, + trait_def_id, + obligation, + substs, + None, + false, + ) + } + + pub(super) fn lookup_op_method_in_trait( + &self, + span: Span, + m_name: Ident, + trait_def_id: DefId, + self_ty: Ty<'tcx>, + opt_input_type: Option<Ty<'tcx>>, + opt_input_expr: Option<&'tcx hir::Expr<'tcx>>, + expected: Expectation<'tcx>, + ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> { + let (obligation, substs) = self.obligation_for_op_method( + span, + trait_def_id, + self_ty, + opt_input_type, + opt_input_expr, + expected, + ); + self.construct_obligation_for_trait( + span, + m_name, + trait_def_id, + obligation, + substs, + opt_input_expr, + true, + ) + } + + // FIXME(#18741): it seems likely that we can consolidate some of this + // code with the other method-lookup code. In particular, the second half + // of this method is basically the same as confirmation. + fn construct_obligation_for_trait( + &self, + span: Span, + m_name: Ident, + trait_def_id: DefId, + obligation: traits::PredicateObligation<'tcx>, + substs: &'tcx ty::List<ty::subst::GenericArg<'tcx>>, + opt_input_expr: Option<&'tcx hir::Expr<'tcx>>, + is_op: bool, + ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> { + debug!(?obligation); + + // Now we want to know if this can be matched + if !self.predicate_may_hold(&obligation) { + debug!("--> Cannot match obligation"); + // Cannot be matched, no such method resolution is possible. + return None; + } + + // Trait must have a method named `m_name` and it should not have + // type parameters or early-bound regions. + let tcx = self.tcx; + let Some(method_item) = self.associated_value(trait_def_id, m_name) else { + tcx.sess.delay_span_bug( + span, + "operator trait does not have corresponding operator method", + ); + return None; + }; + let def_id = method_item.def_id; + let generics = tcx.generics_of(def_id); + assert_eq!(generics.params.len(), 0); + + debug!("lookup_in_trait_adjusted: method_item={:?}", method_item); + let mut obligations = vec![]; + + // Instantiate late-bound regions and substitute the trait + // parameters into the method type to get the actual method type. + // + // N.B., instantiate late-bound regions first so that + // `instantiate_type_scheme` can normalize associated types that + // may reference those regions. + let fn_sig = tcx.bound_fn_sig(def_id); + let fn_sig = fn_sig.subst(self.tcx, substs); + let fn_sig = self.replace_bound_vars_with_fresh_vars(span, infer::FnCall, fn_sig); + + let InferOk { value, obligations: o } = if is_op { + self.normalize_op_associated_types_in_as_infer_ok(span, fn_sig, opt_input_expr) + } else { + self.normalize_associated_types_in_as_infer_ok(span, fn_sig) + }; + let fn_sig = { + obligations.extend(o); + value + }; + + // Register obligations for the parameters. This will include the + // `Self` parameter, which in turn has a bound of the main trait, + // so this also effectively registers `obligation` as well. (We + // used to register `obligation` explicitly, but that resulted in + // double error messages being reported.) + // + // Note that as the method comes from a trait, it should not have + // any late-bound regions appearing in its bounds. + let bounds = self.tcx.predicates_of(def_id).instantiate(self.tcx, substs); + + let InferOk { value, obligations: o } = if is_op { + self.normalize_op_associated_types_in_as_infer_ok(span, bounds, opt_input_expr) + } else { + self.normalize_associated_types_in_as_infer_ok(span, bounds) + }; + let bounds = { + obligations.extend(o); + value + }; + + assert!(!bounds.has_escaping_bound_vars()); + + let cause = if is_op { + ObligationCause::new( + span, + self.body_id, + traits::BinOp { + rhs_span: opt_input_expr.map(|expr| expr.span), + is_lit: opt_input_expr + .map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))), + output_pred: None, + }, + ) + } else { + traits::ObligationCause::misc(span, self.body_id) + }; + obligations.extend(traits::predicates_for_generics(cause.clone(), self.param_env, bounds)); + + // Also add an obligation for the method type being well-formed. + let method_ty = tcx.mk_fn_ptr(ty::Binder::dummy(fn_sig)); + debug!( + "lookup_in_trait_adjusted: matched method method_ty={:?} obligation={:?}", + method_ty, obligation + ); + obligations.push(traits::Obligation::new( + cause, + self.param_env, + ty::Binder::dummy(ty::PredicateKind::WellFormed(method_ty.into())).to_predicate(tcx), + )); + + let callee = MethodCallee { def_id, substs, sig: fn_sig }; + + debug!("callee = {:?}", callee); + + Some(InferOk { obligations, value: callee }) + } + + /// Performs a [full-qualified function call] (formerly "universal function call") lookup. If + /// lookup is successful, it will return the type of definition and the [`DefId`] of the found + /// function definition. + /// + /// [full-qualified function call]: https://doc.rust-lang.org/reference/expressions/call-expr.html#disambiguating-function-calls + /// + /// # Arguments + /// + /// Given a function call like `Foo::bar::<T1,...Tn>(...)`: + /// + /// * `self`: the surrounding `FnCtxt` (!) + /// * `span`: the span of the call, excluding arguments (`Foo::bar::<T1, ...Tn>`) + /// * `method_name`: the identifier of the function within the container type (`bar`) + /// * `self_ty`: the type to search within (`Foo`) + /// * `self_ty_span` the span for the type being searched within (span of `Foo`) + /// * `expr_id`: the [`hir::HirId`] of the expression composing the entire call + #[instrument(level = "debug", skip(self))] + pub fn resolve_fully_qualified_call( + &self, + span: Span, + method_name: Ident, + self_ty: Ty<'tcx>, + self_ty_span: Span, + expr_id: hir::HirId, + ) -> Result<(DefKind, DefId), MethodError<'tcx>> { + debug!( + "resolve_fully_qualified_call: method_name={:?} self_ty={:?} expr_id={:?}", + method_name, self_ty, expr_id, + ); + + let tcx = self.tcx; + + // Check if we have an enum variant. + if let ty::Adt(adt_def, _) = self_ty.kind() { + if adt_def.is_enum() { + let variant_def = adt_def + .variants() + .iter() + .find(|vd| tcx.hygienic_eq(method_name, vd.ident(tcx), adt_def.did())); + if let Some(variant_def) = variant_def { + // Braced variants generate unusable names in value namespace (reserved for + // possible future use), so variants resolved as associated items may refer to + // them as well. It's ok to use the variant's id as a ctor id since an + // error will be reported on any use of such resolution anyway. + let ctor_def_id = variant_def.ctor_def_id.unwrap_or(variant_def.def_id); + tcx.check_stability(ctor_def_id, Some(expr_id), span, Some(method_name.span)); + return Ok(( + DefKind::Ctor(CtorOf::Variant, variant_def.ctor_kind), + ctor_def_id, + )); + } + } + } + + let pick = self.probe_for_name( + span, + probe::Mode::Path, + method_name, + IsSuggestion(false), + self_ty, + expr_id, + ProbeScope::TraitsInScope, + )?; + + self.lint_fully_qualified_call_from_2018( + span, + method_name, + self_ty, + self_ty_span, + expr_id, + &pick, + ); + + debug!("resolve_fully_qualified_call: pick={:?}", pick); + { + let mut typeck_results = self.typeck_results.borrow_mut(); + let used_trait_imports = Lrc::get_mut(&mut typeck_results.used_trait_imports).unwrap(); + for import_id in pick.import_ids { + debug!("resolve_fully_qualified_call: used_trait_import: {:?}", import_id); + used_trait_imports.insert(import_id); + } + } + + let def_kind = pick.item.kind.as_def_kind(); + debug!( + "resolve_fully_qualified_call: def_kind={:?}, def_id={:?}", + def_kind, pick.item.def_id + ); + tcx.check_stability(pick.item.def_id, Some(expr_id), span, Some(method_name.span)); + Ok((def_kind, pick.item.def_id)) + } + + /// Finds item with name `item_name` defined in impl/trait `def_id` + /// and return it, or `None`, if no such item was defined there. + pub fn associated_value(&self, def_id: DefId, item_name: Ident) -> Option<ty::AssocItem> { + self.tcx + .associated_items(def_id) + .find_by_name_and_namespace(self.tcx, item_name, Namespace::ValueNS, def_id) + .copied() + } +} diff --git a/compiler/rustc_typeck/src/check/method/prelude2021.rs b/compiler/rustc_typeck/src/check/method/prelude2021.rs new file mode 100644 index 000000000..7c68d9304 --- /dev/null +++ b/compiler/rustc_typeck/src/check/method/prelude2021.rs @@ -0,0 +1,419 @@ +use hir::def_id::DefId; +use hir::HirId; +use hir::ItemKind; +use rustc_ast::Mutability; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_middle::ty::subst::InternalSubsts; +use rustc_middle::ty::{Adt, Array, Ref, Ty}; +use rustc_session::lint::builtin::RUST_2021_PRELUDE_COLLISIONS; +use rustc_span::symbol::kw::{Empty, Underscore}; +use rustc_span::symbol::{sym, Ident}; +use rustc_span::Span; +use rustc_trait_selection::infer::InferCtxtExt; + +use crate::check::{ + method::probe::{self, Pick}, + FnCtxt, +}; + +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + pub(super) fn lint_dot_call_from_2018( + &self, + self_ty: Ty<'tcx>, + segment: &hir::PathSegment<'_>, + span: Span, + call_expr: &'tcx hir::Expr<'tcx>, + self_expr: &'tcx hir::Expr<'tcx>, + pick: &Pick<'tcx>, + args: &'tcx [hir::Expr<'tcx>], + ) { + debug!( + "lookup(method_name={}, self_ty={:?}, call_expr={:?}, self_expr={:?})", + segment.ident, self_ty, call_expr, self_expr + ); + + // Rust 2021 and later is already using the new prelude + if span.rust_2021() { + return; + } + + let prelude_or_array_lint = match segment.ident.name { + // `try_into` was added to the prelude in Rust 2021. + sym::try_into => RUST_2021_PRELUDE_COLLISIONS, + // `into_iter` wasn't added to the prelude, + // but `[T; N].into_iter()` doesn't resolve to IntoIterator::into_iter + // before Rust 2021, which results in the same problem. + // It is only a problem for arrays. + sym::into_iter if let Array(..) = self_ty.kind() => { + // In this case, it wasn't really a prelude addition that was the problem. + // Instead, the problem is that the array-into_iter hack will no longer apply in Rust 2021. + rustc_lint::ARRAY_INTO_ITER + } + _ => return, + }; + + // No need to lint if method came from std/core, as that will now be in the prelude + if matches!(self.tcx.crate_name(pick.item.def_id.krate), sym::std | sym::core) { + return; + } + + if matches!(pick.kind, probe::PickKind::InherentImplPick | probe::PickKind::ObjectPick) { + // avoid repeatedly adding unneeded `&*`s + if pick.autoderefs == 1 + && matches!( + pick.autoref_or_ptr_adjustment, + Some(probe::AutorefOrPtrAdjustment::Autoref { .. }) + ) + && matches!(self_ty.kind(), Ref(..)) + { + return; + } + + // if it's an inherent `self` method (not `&self` or `&mut self`), it will take + // precedence over the `TryInto` impl, and thus won't break in 2021 edition + if pick.autoderefs == 0 && pick.autoref_or_ptr_adjustment.is_none() { + return; + } + + // Inherent impls only require not relying on autoref and autoderef in order to + // ensure that the trait implementation won't be used + self.tcx.struct_span_lint_hir( + prelude_or_array_lint, + self_expr.hir_id, + self_expr.span, + |lint| { + let sp = self_expr.span; + + let mut lint = lint.build(&format!( + "trait method `{}` will become ambiguous in Rust 2021", + segment.ident.name + )); + + let derefs = "*".repeat(pick.autoderefs); + + let autoref = match pick.autoref_or_ptr_adjustment { + Some(probe::AutorefOrPtrAdjustment::Autoref { + mutbl: Mutability::Mut, + .. + }) => "&mut ", + Some(probe::AutorefOrPtrAdjustment::Autoref { + mutbl: Mutability::Not, + .. + }) => "&", + Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "", + }; + if let Ok(self_expr) = self.sess().source_map().span_to_snippet(self_expr.span) + { + let self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) = + pick.autoref_or_ptr_adjustment + { + format!("{}{} as *const _", derefs, self_expr) + } else { + format!("{}{}{}", autoref, derefs, self_expr) + }; + + lint.span_suggestion( + sp, + "disambiguate the method call", + format!("({})", self_adjusted), + Applicability::MachineApplicable, + ); + } else { + let self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) = + pick.autoref_or_ptr_adjustment + { + format!("{}(...) as *const _", derefs) + } else { + format!("{}{}...", autoref, derefs) + }; + lint.span_help( + sp, + &format!("disambiguate the method call with `({})`", self_adjusted,), + ); + } + + lint.emit(); + }, + ); + } else { + // trait implementations require full disambiguation to not clash with the new prelude + // additions (i.e. convert from dot-call to fully-qualified call) + self.tcx.struct_span_lint_hir( + prelude_or_array_lint, + call_expr.hir_id, + call_expr.span, + |lint| { + let sp = call_expr.span; + let trait_name = self.trait_path_or_bare_name( + span, + call_expr.hir_id, + pick.item.container_id(self.tcx), + ); + + let mut lint = lint.build(&format!( + "trait method `{}` will become ambiguous in Rust 2021", + segment.ident.name + )); + + let (self_adjusted, precise) = self.adjust_expr(pick, self_expr, sp); + if precise { + let args = args + .iter() + .skip(1) + .map(|arg| { + let span = arg.span.find_ancestor_inside(sp).unwrap_or_default(); + format!( + ", {}", + self.sess().source_map().span_to_snippet(span).unwrap() + ) + }) + .collect::<String>(); + + lint.span_suggestion( + sp, + "disambiguate the associated function", + format!( + "{}::{}{}({}{})", + trait_name, + segment.ident.name, + if let Some(args) = segment.args.as_ref().and_then(|args| self + .sess() + .source_map() + .span_to_snippet(args.span_ext) + .ok()) + { + // Keep turbofish. + format!("::{}", args) + } else { + String::new() + }, + self_adjusted, + args, + ), + Applicability::MachineApplicable, + ); + } else { + lint.span_help( + sp, + &format!( + "disambiguate the associated function with `{}::{}(...)`", + trait_name, segment.ident, + ), + ); + } + + lint.emit(); + }, + ); + } + } + + pub(super) fn lint_fully_qualified_call_from_2018( + &self, + span: Span, + method_name: Ident, + self_ty: Ty<'tcx>, + self_ty_span: Span, + expr_id: hir::HirId, + pick: &Pick<'tcx>, + ) { + // Rust 2021 and later is already using the new prelude + if span.rust_2021() { + return; + } + + // These are the fully qualified methods added to prelude in Rust 2021 + if !matches!(method_name.name, sym::try_into | sym::try_from | sym::from_iter) { + return; + } + + // No need to lint if method came from std/core, as that will now be in the prelude + if matches!(self.tcx.crate_name(pick.item.def_id.krate), sym::std | sym::core) { + return; + } + + // For from_iter, check if the type actually implements FromIterator. + // If we know it does not, we don't need to warn. + if method_name.name == sym::from_iter { + if let Some(trait_def_id) = self.tcx.get_diagnostic_item(sym::FromIterator) { + if !self + .infcx + .type_implements_trait( + trait_def_id, + self_ty, + InternalSubsts::empty(), + self.param_env, + ) + .may_apply() + { + return; + } + } + } + + // No need to lint if this is an inherent method called on a specific type, like `Vec::foo(...)`, + // since such methods take precedence over trait methods. + if matches!(pick.kind, probe::PickKind::InherentImplPick) { + return; + } + + self.tcx.struct_span_lint_hir(RUST_2021_PRELUDE_COLLISIONS, expr_id, span, |lint| { + // "type" refers to either a type or, more likely, a trait from which + // the associated function or method is from. + let container_id = pick.item.container_id(self.tcx); + let trait_path = self.trait_path_or_bare_name(span, expr_id, container_id); + let trait_generics = self.tcx.generics_of(container_id); + + let trait_name = + if trait_generics.params.len() <= trait_generics.has_self as usize { + trait_path + } else { + let counts = trait_generics.own_counts(); + format!( + "{}<{}>", + trait_path, + std::iter::repeat("'_") + .take(counts.lifetimes) + .chain(std::iter::repeat("_").take( + counts.types + counts.consts - trait_generics.has_self as usize + )) + .collect::<Vec<_>>() + .join(", ") + ) + }; + + let mut lint = lint.build(&format!( + "trait-associated function `{}` will become ambiguous in Rust 2021", + method_name.name + )); + + let mut self_ty_name = self_ty_span + .find_ancestor_inside(span) + .and_then(|span| self.sess().source_map().span_to_snippet(span).ok()) + .unwrap_or_else(|| self_ty.to_string()); + + // Get the number of generics the self type has (if an Adt) unless we can determine that + // the user has written the self type with generics already which we (naively) do by looking + // for a "<" in `self_ty_name`. + if !self_ty_name.contains('<') { + if let Adt(def, _) = self_ty.kind() { + let generics = self.tcx.generics_of(def.did()); + if !generics.params.is_empty() { + let counts = generics.own_counts(); + self_ty_name += &format!( + "<{}>", + std::iter::repeat("'_") + .take(counts.lifetimes) + .chain(std::iter::repeat("_").take(counts.types + counts.consts)) + .collect::<Vec<_>>() + .join(", ") + ); + } + } + } + lint.span_suggestion( + span, + "disambiguate the associated function", + format!("<{} as {}>::{}", self_ty_name, trait_name, method_name.name,), + Applicability::MachineApplicable, + ); + + lint.emit(); + }); + } + + fn trait_path_or_bare_name( + &self, + span: Span, + expr_hir_id: HirId, + trait_def_id: DefId, + ) -> String { + self.trait_path(span, expr_hir_id, trait_def_id).unwrap_or_else(|| { + let key = self.tcx.def_key(trait_def_id); + format!("{}", key.disambiguated_data.data) + }) + } + + fn trait_path(&self, span: Span, expr_hir_id: HirId, trait_def_id: DefId) -> Option<String> { + let applicable_traits = self.tcx.in_scope_traits(expr_hir_id)?; + let applicable_trait = applicable_traits.iter().find(|t| t.def_id == trait_def_id)?; + if applicable_trait.import_ids.is_empty() { + // The trait was declared within the module, we only need to use its name. + return None; + } + + let import_items: Vec<_> = applicable_trait + .import_ids + .iter() + .map(|&import_id| self.tcx.hir().expect_item(import_id)) + .collect(); + + // Find an identifier with which this trait was imported (note that `_` doesn't count). + let any_id = import_items + .iter() + .filter_map(|item| if item.ident.name != Underscore { Some(item.ident) } else { None }) + .next(); + if let Some(any_id) = any_id { + if any_id.name == Empty { + // Glob import, so just use its name. + return None; + } else { + return Some(format!("{}", any_id)); + } + } + + // All that is left is `_`! We need to use the full path. It doesn't matter which one we pick, + // so just take the first one. + match import_items[0].kind { + ItemKind::Use(path, _) => Some( + path.segments + .iter() + .map(|segment| segment.ident.to_string()) + .collect::<Vec<_>>() + .join("::"), + ), + _ => { + span_bug!(span, "unexpected item kind, expected a use: {:?}", import_items[0].kind); + } + } + } + + /// Creates a string version of the `expr` that includes explicit adjustments. + /// Returns the string and also a bool indicating whether this is a *precise* + /// suggestion. + fn adjust_expr( + &self, + pick: &Pick<'tcx>, + expr: &hir::Expr<'tcx>, + outer: Span, + ) -> (String, bool) { + let derefs = "*".repeat(pick.autoderefs); + + let autoref = match pick.autoref_or_ptr_adjustment { + Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl: Mutability::Mut, .. }) => "&mut ", + Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl: Mutability::Not, .. }) => "&", + Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "", + }; + + let (expr_text, precise) = if let Some(expr_text) = expr + .span + .find_ancestor_inside(outer) + .and_then(|span| self.sess().source_map().span_to_snippet(span).ok()) + { + (expr_text, true) + } else { + ("(..)".to_string(), false) + }; + + let adjusted_text = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) = + pick.autoref_or_ptr_adjustment + { + format!("{}{} as *const _", derefs, expr_text) + } else { + format!("{}{}{}", autoref, derefs, expr_text) + }; + + (adjusted_text, precise) + } +} diff --git a/compiler/rustc_typeck/src/check/method/probe.rs b/compiler/rustc_typeck/src/check/method/probe.rs new file mode 100644 index 000000000..efe15fec7 --- /dev/null +++ b/compiler/rustc_typeck/src/check/method/probe.rs @@ -0,0 +1,1932 @@ +use super::suggest; +use super::CandidateSource; +use super::MethodError; +use super::NoMatchData; + +use crate::check::FnCtxt; +use crate::errors::MethodCallOnUnknownType; +use crate::hir::def::DefKind; +use crate::hir::def_id::DefId; + +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::def::Namespace; +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::subst::{InternalSubsts, Subst, SubstsRef}; +use rustc_middle::ty::GenericParamDefKind; +use rustc_middle::ty::{self, ParamEnvAnd, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeVisitable}; +use rustc_session::lint; +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::symbol::sym; +use rustc_span::{symbol::Ident, Span, Symbol, DUMMY_SP}; +use rustc_trait_selection::autoderef::{self, Autoderef}; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; +use rustc_trait_selection::traits::query::method_autoderef::MethodAutoderefBadTy; +use rustc_trait_selection::traits::query::method_autoderef::{ + CandidateStep, MethodAutoderefStepsResult, +}; +use rustc_trait_selection::traits::query::CanonicalTyGoal; +use rustc_trait_selection::traits::{self, ObligationCause}; +use std::cmp::max; +use std::iter; +use std::mem; +use std::ops::Deref; + +use smallvec::{smallvec, SmallVec}; + +use self::CandidateKind::*; +pub use self::PickKind::*; + +/// Boolean flag used to indicate if this search is for a suggestion +/// or not. If true, we can allow ambiguity and so forth. +#[derive(Clone, Copy, Debug)] +pub struct IsSuggestion(pub bool); + +struct ProbeContext<'a, 'tcx> { + fcx: &'a FnCtxt<'a, 'tcx>, + span: Span, + mode: Mode, + method_name: Option<Ident>, + return_type: Option<Ty<'tcx>>, + + /// This is the OriginalQueryValues for the steps queries + /// that are answered in steps. + orig_steps_var_values: OriginalQueryValues<'tcx>, + steps: &'tcx [CandidateStep<'tcx>], + + inherent_candidates: Vec<Candidate<'tcx>>, + extension_candidates: Vec<Candidate<'tcx>>, + impl_dups: FxHashSet<DefId>, + + /// Collects near misses when the candidate functions are missing a `self` keyword and is only + /// used for error reporting + static_candidates: Vec<CandidateSource>, + + /// When probing for names, include names that are close to the + /// requested name (by Levensthein distance) + allow_similar_names: bool, + + /// Some(candidate) if there is a private candidate + private_candidate: Option<(DefKind, DefId)>, + + /// Collects near misses when trait bounds for type parameters are unsatisfied and is only used + /// for error reporting + unsatisfied_predicates: + Vec<(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>, Option<ObligationCause<'tcx>>)>, + + is_suggestion: IsSuggestion, + + scope_expr_id: hir::HirId, +} + +impl<'a, 'tcx> Deref for ProbeContext<'a, 'tcx> { + type Target = FnCtxt<'a, 'tcx>; + fn deref(&self) -> &Self::Target { + self.fcx + } +} + +#[derive(Debug, Clone)] +struct Candidate<'tcx> { + // Candidates are (I'm not quite sure, but they are mostly) basically + // some metadata on top of a `ty::AssocItem` (without substs). + // + // However, method probing wants to be able to evaluate the predicates + // for a function with the substs applied - for example, if a function + // has `where Self: Sized`, we don't want to consider it unless `Self` + // is actually `Sized`, and similarly, return-type suggestions want + // to consider the "actual" return type. + // + // The way this is handled is through `xform_self_ty`. It contains + // the receiver type of this candidate, but `xform_self_ty`, + // `xform_ret_ty` and `kind` (which contains the predicates) have the + // generic parameters of this candidate substituted with the *same set* + // of inference variables, which acts as some weird sort of "query". + // + // When we check out a candidate, we require `xform_self_ty` to be + // a subtype of the passed-in self-type, and this equates the type + // variables in the rest of the fields. + // + // For example, if we have this candidate: + // ``` + // trait Foo { + // fn foo(&self) where Self: Sized; + // } + // ``` + // + // Then `xform_self_ty` will be `&'erased ?X` and `kind` will contain + // the predicate `?X: Sized`, so if we are evaluating `Foo` for a + // the receiver `&T`, we'll do the subtyping which will make `?X` + // get the right value, then when we evaluate the predicate we'll check + // if `T: Sized`. + xform_self_ty: Ty<'tcx>, + xform_ret_ty: Option<Ty<'tcx>>, + item: ty::AssocItem, + kind: CandidateKind<'tcx>, + import_ids: SmallVec<[LocalDefId; 1]>, +} + +#[derive(Debug, Clone)] +enum CandidateKind<'tcx> { + InherentImplCandidate( + SubstsRef<'tcx>, + // Normalize obligations + Vec<traits::PredicateObligation<'tcx>>, + ), + ObjectCandidate, + TraitCandidate(ty::TraitRef<'tcx>), + WhereClauseCandidate( + // Trait + ty::PolyTraitRef<'tcx>, + ), +} + +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +enum ProbeResult { + NoMatch, + BadReturnType, + Match, +} + +/// When adjusting a receiver we often want to do one of +/// +/// - Add a `&` (or `&mut`), converting the receiver from `T` to `&T` (or `&mut T`) +/// - If the receiver has type `*mut T`, convert it to `*const T` +/// +/// This type tells us which one to do. +/// +/// Note that in principle we could do both at the same time. For example, when the receiver has +/// type `T`, we could autoref it to `&T`, then convert to `*const T`. Or, when it has type `*mut +/// T`, we could convert it to `*const T`, then autoref to `&*const T`. However, currently we do +/// (at most) one of these. Either the receiver has type `T` and we convert it to `&T` (or with +/// `mut`), or it has type `*mut T` and we convert it to `*const T`. +#[derive(Debug, PartialEq, Copy, Clone)] +pub enum AutorefOrPtrAdjustment { + /// Receiver has type `T`, add `&` or `&mut` (it `T` is `mut`), and maybe also "unsize" it. + /// Unsizing is used to convert a `[T; N]` to `[T]`, which only makes sense when autorefing. + Autoref { + mutbl: hir::Mutability, + + /// Indicates that the source expression should be "unsized" to a target type. + /// This is special-cased for just arrays unsizing to slices. + unsize: bool, + }, + /// Receiver has type `*mut T`, convert to `*const T` + ToConstPtr, +} + +impl AutorefOrPtrAdjustment { + fn get_unsize(&self) -> bool { + match self { + AutorefOrPtrAdjustment::Autoref { mutbl: _, unsize } => *unsize, + AutorefOrPtrAdjustment::ToConstPtr => false, + } + } +} + +#[derive(Debug, PartialEq, Clone)] +pub struct Pick<'tcx> { + pub item: ty::AssocItem, + pub kind: PickKind<'tcx>, + pub import_ids: SmallVec<[LocalDefId; 1]>, + + /// Indicates that the source expression should be autoderef'd N times + /// ```ignore (not-rust) + /// A = expr | *expr | **expr | ... + /// ``` + pub autoderefs: usize, + + /// Indicates that we want to add an autoref (and maybe also unsize it), or if the receiver is + /// `*mut T`, convert it to `*const T`. + pub autoref_or_ptr_adjustment: Option<AutorefOrPtrAdjustment>, + pub self_ty: Ty<'tcx>, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum PickKind<'tcx> { + InherentImplPick, + ObjectPick, + TraitPick, + WhereClausePick( + // Trait + ty::PolyTraitRef<'tcx>, + ), +} + +pub type PickResult<'tcx> = Result<Pick<'tcx>, MethodError<'tcx>>; + +#[derive(PartialEq, Eq, Copy, Clone, Debug)] +pub enum Mode { + // An expression of the form `receiver.method_name(...)`. + // Autoderefs are performed on `receiver`, lookup is done based on the + // `self` argument of the method, and static methods aren't considered. + MethodCall, + // An expression of the form `Type::item` or `<T>::item`. + // No autoderefs are performed, lookup is done based on the type each + // implementation is for, and static methods are included. + Path, +} + +#[derive(PartialEq, Eq, Copy, Clone, Debug)] +pub enum ProbeScope { + // Assemble candidates coming only from traits in scope. + TraitsInScope, + + // Assemble candidates coming from all traits. + AllTraits, +} + +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + /// This is used to offer suggestions to users. It returns methods + /// that could have been called which have the desired return + /// type. Some effort is made to rule out methods that, if called, + /// would result in an error (basically, the same criteria we + /// would use to decide if a method is a plausible fit for + /// ambiguity purposes). + #[instrument(level = "debug", skip(self, scope_expr_id))] + pub fn probe_for_return_type( + &self, + span: Span, + mode: Mode, + return_type: Ty<'tcx>, + self_ty: Ty<'tcx>, + scope_expr_id: hir::HirId, + ) -> Vec<ty::AssocItem> { + debug!( + "probe(self_ty={:?}, return_type={}, scope_expr_id={})", + self_ty, return_type, scope_expr_id + ); + let method_names = self + .probe_op( + span, + mode, + None, + Some(return_type), + IsSuggestion(true), + self_ty, + scope_expr_id, + ProbeScope::AllTraits, + |probe_cx| Ok(probe_cx.candidate_method_names()), + ) + .unwrap_or_default(); + method_names + .iter() + .flat_map(|&method_name| { + self.probe_op( + span, + mode, + Some(method_name), + Some(return_type), + IsSuggestion(true), + self_ty, + scope_expr_id, + ProbeScope::AllTraits, + |probe_cx| probe_cx.pick(), + ) + .ok() + .map(|pick| pick.item) + }) + .collect() + } + + #[instrument(level = "debug", skip(self, scope_expr_id))] + pub fn probe_for_name( + &self, + span: Span, + mode: Mode, + item_name: Ident, + is_suggestion: IsSuggestion, + self_ty: Ty<'tcx>, + scope_expr_id: hir::HirId, + scope: ProbeScope, + ) -> PickResult<'tcx> { + debug!( + "probe(self_ty={:?}, item_name={}, scope_expr_id={})", + self_ty, item_name, scope_expr_id + ); + self.probe_op( + span, + mode, + Some(item_name), + None, + is_suggestion, + self_ty, + scope_expr_id, + scope, + |probe_cx| probe_cx.pick(), + ) + } + + fn probe_op<OP, R>( + &'a self, + span: Span, + mode: Mode, + method_name: Option<Ident>, + return_type: Option<Ty<'tcx>>, + is_suggestion: IsSuggestion, + self_ty: Ty<'tcx>, + scope_expr_id: hir::HirId, + scope: ProbeScope, + op: OP, + ) -> Result<R, MethodError<'tcx>> + where + OP: FnOnce(ProbeContext<'a, 'tcx>) -> Result<R, MethodError<'tcx>>, + { + let mut orig_values = OriginalQueryValues::default(); + let param_env_and_self_ty = self.canonicalize_query( + ParamEnvAnd { param_env: self.param_env, value: self_ty }, + &mut orig_values, + ); + + let steps = if mode == Mode::MethodCall { + self.tcx.method_autoderef_steps(param_env_and_self_ty) + } else { + self.probe(|_| { + // Mode::Path - the deref steps is "trivial". This turns + // our CanonicalQuery into a "trivial" QueryResponse. This + // is a bit inefficient, but I don't think that writing + // special handling for this "trivial case" is a good idea. + + let infcx = &self.infcx; + let (ParamEnvAnd { param_env: _, value: self_ty }, canonical_inference_vars) = + infcx.instantiate_canonical_with_fresh_inference_vars( + span, + ¶m_env_and_self_ty, + ); + debug!( + "probe_op: Mode::Path, param_env_and_self_ty={:?} self_ty={:?}", + param_env_and_self_ty, self_ty + ); + MethodAutoderefStepsResult { + steps: infcx.tcx.arena.alloc_from_iter([CandidateStep { + self_ty: self.make_query_response_ignoring_pending_obligations( + canonical_inference_vars, + self_ty, + ), + autoderefs: 0, + from_unsafe_deref: false, + unsize: false, + }]), + opt_bad_ty: None, + reached_recursion_limit: false, + } + }) + }; + + // If our autoderef loop had reached the recursion limit, + // report an overflow error, but continue going on with + // the truncated autoderef list. + if steps.reached_recursion_limit { + self.probe(|_| { + let ty = &steps + .steps + .last() + .unwrap_or_else(|| span_bug!(span, "reached the recursion limit in 0 steps?")) + .self_ty; + let ty = self + .probe_instantiate_query_response(span, &orig_values, ty) + .unwrap_or_else(|_| span_bug!(span, "instantiating {:?} failed?", ty)); + autoderef::report_autoderef_recursion_limit_error(self.tcx, span, ty.value); + }); + } + + // If we encountered an `_` type or an error type during autoderef, this is + // ambiguous. + if let Some(bad_ty) = &steps.opt_bad_ty { + if is_suggestion.0 { + // Ambiguity was encountered during a suggestion. Just keep going. + debug!("ProbeContext: encountered ambiguity in suggestion"); + } else if bad_ty.reached_raw_pointer && !self.tcx.features().arbitrary_self_types { + // this case used to be allowed by the compiler, + // so we do a future-compat lint here for the 2015 edition + // (see https://github.com/rust-lang/rust/issues/46906) + if self.tcx.sess.rust_2018() { + self.tcx.sess.emit_err(MethodCallOnUnknownType { span }); + } else { + self.tcx.struct_span_lint_hir( + lint::builtin::TYVAR_BEHIND_RAW_POINTER, + scope_expr_id, + span, + |lint| { + lint.build("type annotations needed").emit(); + }, + ); + } + } else { + // Encountered a real ambiguity, so abort the lookup. If `ty` is not + // an `Err`, report the right "type annotations needed" error pointing + // to it. + let ty = &bad_ty.ty; + let ty = self + .probe_instantiate_query_response(span, &orig_values, ty) + .unwrap_or_else(|_| span_bug!(span, "instantiating {:?} failed?", ty)); + let ty = self.structurally_resolved_type(span, ty.value); + assert!(matches!(ty.kind(), ty::Error(_))); + return Err(MethodError::NoMatch(NoMatchData { + static_candidates: Vec::new(), + unsatisfied_predicates: Vec::new(), + out_of_scope_traits: Vec::new(), + lev_candidate: None, + mode, + })); + } + } + + debug!("ProbeContext: steps for self_ty={:?} are {:?}", self_ty, steps); + + // this creates one big transaction so that all type variables etc + // that we create during the probe process are removed later + self.probe(|_| { + let mut probe_cx = ProbeContext::new( + self, + span, + mode, + method_name, + return_type, + orig_values, + steps.steps, + is_suggestion, + scope_expr_id, + ); + + probe_cx.assemble_inherent_candidates(); + match scope { + ProbeScope::TraitsInScope => { + probe_cx.assemble_extension_candidates_for_traits_in_scope(scope_expr_id) + } + ProbeScope::AllTraits => probe_cx.assemble_extension_candidates_for_all_traits(), + }; + op(probe_cx) + }) + } +} + +pub fn provide(providers: &mut ty::query::Providers) { + providers.method_autoderef_steps = method_autoderef_steps; +} + +fn method_autoderef_steps<'tcx>( + tcx: TyCtxt<'tcx>, + goal: CanonicalTyGoal<'tcx>, +) -> MethodAutoderefStepsResult<'tcx> { + debug!("method_autoderef_steps({:?})", goal); + + tcx.infer_ctxt().enter_with_canonical(DUMMY_SP, &goal, |ref infcx, goal, inference_vars| { + let ParamEnvAnd { param_env, value: self_ty } = goal; + + let mut autoderef = + Autoderef::new(infcx, param_env, hir::CRATE_HIR_ID, DUMMY_SP, self_ty, DUMMY_SP) + .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, + ), + autoderefs: d, + from_unsafe_deref: reached_raw_pointer, + unsize: false, + }; + if let ty::RawPtr(_) = ty.kind() { + // all the subsequent steps will be from_unsafe_deref + reached_raw_pointer = true; + } + step + }) + .collect(); + + let final_ty = autoderef.final_ty(true); + let opt_bad_ty = match final_ty.kind() { + ty::Infer(ty::TyVar(_)) | ty::Error(_) => Some(MethodAutoderefBadTy { + reached_raw_pointer, + ty: infcx + .make_query_response_ignoring_pending_obligations(inference_vars, final_ty), + }), + ty::Array(elem_ty, _) => { + let dereferences = steps.len() - 1; + + steps.push(CandidateStep { + self_ty: infcx.make_query_response_ignoring_pending_obligations( + inference_vars, + infcx.tcx.mk_slice(*elem_ty), + ), + autoderefs: dereferences, + // this could be from an unsafe deref if we had + // a *mut/const [T; N] + from_unsafe_deref: reached_raw_pointer, + unsize: true, + }); + + None + } + _ => None, + }; + + debug!("method_autoderef_steps: steps={:?} opt_bad_ty={:?}", steps, opt_bad_ty); + + MethodAutoderefStepsResult { + steps: tcx.arena.alloc_from_iter(steps), + opt_bad_ty: opt_bad_ty.map(|ty| &*tcx.arena.alloc(ty)), + reached_recursion_limit: autoderef.reached_recursion_limit(), + } + }) +} + +impl<'a, 'tcx> ProbeContext<'a, 'tcx> { + fn new( + fcx: &'a FnCtxt<'a, 'tcx>, + span: Span, + mode: Mode, + method_name: Option<Ident>, + return_type: Option<Ty<'tcx>>, + orig_steps_var_values: OriginalQueryValues<'tcx>, + steps: &'tcx [CandidateStep<'tcx>], + is_suggestion: IsSuggestion, + scope_expr_id: hir::HirId, + ) -> ProbeContext<'a, 'tcx> { + ProbeContext { + fcx, + span, + mode, + method_name, + return_type, + inherent_candidates: Vec::new(), + extension_candidates: Vec::new(), + impl_dups: FxHashSet::default(), + orig_steps_var_values, + steps, + static_candidates: Vec::new(), + allow_similar_names: false, + private_candidate: None, + unsatisfied_predicates: Vec::new(), + is_suggestion, + scope_expr_id, + } + } + + fn reset(&mut self) { + self.inherent_candidates.clear(); + self.extension_candidates.clear(); + self.impl_dups.clear(); + self.static_candidates.clear(); + self.private_candidate = None; + } + + /////////////////////////////////////////////////////////////////////////// + // CANDIDATE ASSEMBLY + + 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; + item.visibility(self.tcx).is_accessible_from(def_scope, self.tcx) + } else { + true + }; + if is_accessible { + if is_inherent { + self.inherent_candidates.push(candidate); + } else { + self.extension_candidates.push(candidate); + } + } else if self.private_candidate.is_none() { + self.private_candidate = + Some((candidate.item.kind.as_def_kind(), candidate.item.def_id)); + } + } + + fn assemble_inherent_candidates(&mut self) { + for step in self.steps.iter() { + self.assemble_probe(&step.self_ty); + } + } + + fn assemble_probe(&mut self, self_ty: &Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>) { + debug!("assemble_probe: self_ty={:?}", self_ty); + let raw_self_ty = self_ty.value.value; + match *raw_self_ty.kind() { + ty::Dynamic(data, ..) if let Some(p) = data.principal() => { + // Subtle: we can't use `instantiate_query_response` here: using it will + // commit to all of the type equalities assumed by inference going through + // autoderef (see the `method-probe-no-guessing` test). + // + // However, in this code, it is OK if we end up with an object type that is + // "more general" than the object type that we are evaluating. For *every* + // object type `MY_OBJECT`, a function call that goes through a trait-ref + // of the form `<MY_OBJECT as SuperTraitOf(MY_OBJECT)>::func` is a valid + // `ObjectCandidate`, and it should be discoverable "exactly" through one + // of the iterations in the autoderef loop, so there is no problem with it + // being discoverable in another one of these iterations. + // + // Using `instantiate_canonical_with_fresh_inference_vars` on our + // `Canonical<QueryResponse<Ty<'tcx>>>` and then *throwing away* the + // `CanonicalVarValues` will exactly give us such a generalization - it + // will still match the original object type, but it won't pollute our + // type variables in any form, so just do that! + let (QueryResponse { value: generalized_self_ty, .. }, _ignored_var_values) = + self.fcx + .instantiate_canonical_with_fresh_inference_vars(self.span, self_ty); + + self.assemble_inherent_candidates_from_object(generalized_self_ty); + self.assemble_inherent_impl_candidates_for_type(p.def_id()); + if self.tcx.has_attr(p.def_id(), sym::rustc_has_incoherent_inherent_impls) { + self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty); + } + } + ty::Adt(def, _) => { + let def_id = def.did(); + self.assemble_inherent_impl_candidates_for_type(def_id); + if self.tcx.has_attr(def_id, sym::rustc_has_incoherent_inherent_impls) { + self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty); + } + } + ty::Foreign(did) => { + self.assemble_inherent_impl_candidates_for_type(did); + if self.tcx.has_attr(did, sym::rustc_has_incoherent_inherent_impls) { + self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty); + } + } + ty::Param(p) => { + self.assemble_inherent_candidates_from_param(p); + } + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Str + | ty::Array(..) + | ty::Slice(_) + | ty::RawPtr(_) + | ty::Ref(..) + | ty::Never + | ty::Tuple(..) => self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty), + _ => {} + } + } + + fn assemble_inherent_candidates_for_incoherent_ty(&mut self, self_ty: Ty<'tcx>) { + let Some(simp) = simplify_type(self.tcx, self_ty, TreatParams::AsInfer) else { + bug!("unexpected incoherent type: {:?}", self_ty) + }; + for &impl_def_id in self.tcx.incoherent_impls(simp) { + self.assemble_inherent_impl_probe(impl_def_id); + } + } + + fn assemble_inherent_impl_candidates_for_type(&mut self, def_id: DefId) { + let impl_def_ids = self.tcx.at(self.span).inherent_impls(def_id); + for &impl_def_id in impl_def_ids.iter() { + self.assemble_inherent_impl_probe(impl_def_id); + } + } + + fn assemble_inherent_impl_probe(&mut self, impl_def_id: DefId) { + if !self.impl_dups.insert(impl_def_id) { + return; // already visited + } + + debug!("assemble_inherent_impl_probe {:?}", impl_def_id); + + for item in self.impl_or_trait_item(impl_def_id) { + if !self.has_applicable_self(&item) { + // No receiver declared. Not a candidate. + self.record_static_candidate(CandidateSource::Impl(impl_def_id)); + continue; + } + + let (impl_ty, impl_substs) = self.impl_ty_and_substs(impl_def_id); + let impl_ty = impl_ty.subst(self.tcx, impl_substs); + + 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); + 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 + // fcx's fulfillment context after this probe is over. + // Note: we only normalize `xform_self_ty` here since the normalization + // of the return type can lead to inference results that prohibit + // valid candidates from being found, see issue #85671 + // FIXME Postponing the normalization of the return type likely only hides a deeper bug, + // which might be caused by the `param_env` itself. The clauses of the `param_env` + // maybe shouldn't include `Param`s, but rather fresh variables or be canonicalized, + // see issue #89650 + let cause = traits::ObligationCause::misc(self.span, self.body_id); + let selcx = &mut traits::SelectionContext::new(self.fcx); + let traits::Normalized { value: xform_self_ty, obligations } = + traits::normalize(selcx, self.param_env, cause, xform_self_ty); + debug!( + "assemble_inherent_impl_probe after normalization: xform_self_ty = {:?}/{:?}", + xform_self_ty, xform_ret_ty + ); + + self.push_candidate( + Candidate { + xform_self_ty, + xform_ret_ty, + item, + kind: InherentImplCandidate(impl_substs, obligations), + import_ids: smallvec![], + }, + true, + ); + } + } + + fn assemble_inherent_candidates_from_object(&mut self, self_ty: Ty<'tcx>) { + debug!("assemble_inherent_candidates_from_object(self_ty={:?})", self_ty); + + let principal = match self_ty.kind() { + ty::Dynamic(ref data, ..) => Some(data), + _ => None, + } + .and_then(|data| data.principal()) + .unwrap_or_else(|| { + span_bug!( + self.span, + "non-object {:?} in assemble_inherent_candidates_from_object", + self_ty + ) + }); + + // It is illegal to invoke a method on a trait instance that refers to + // the `Self` type. An [`ObjectSafetyViolation::SupertraitSelf`] error + // will be reported by `object_safety.rs` if the method refers to the + // `Self` type anywhere other than the receiver. Here, we use a + // substitution that replaces `Self` with the object type itself. Hence, + // a `&self` method will wind up with an argument type like `&dyn Trait`. + let trait_ref = principal.with_self_ty(self.tcx, self_ty); + self.elaborate_bounds(iter::once(trait_ref), |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, + kind: ObjectCandidate, + import_ids: smallvec![], + }, + true, + ); + }); + } + + fn assemble_inherent_candidates_from_param(&mut self, param_ty: ty::ParamTy) { + // FIXME: do we want to commit to this behavior for param bounds? + debug!("assemble_inherent_candidates_from_param(param_ty={:?})", param_ty); + + let bounds = self.param_env.caller_bounds().iter().filter_map(|predicate| { + let bound_predicate = predicate.kind(); + match bound_predicate.skip_binder() { + ty::PredicateKind::Trait(trait_predicate) => { + match *trait_predicate.trait_ref.self_ty().kind() { + ty::Param(p) if p == param_ty => { + Some(bound_predicate.rebind(trait_predicate.trait_ref)) + } + _ => None, + } + } + ty::PredicateKind::Subtype(..) + | ty::PredicateKind::Coerce(..) + | ty::PredicateKind::Projection(..) + | ty::PredicateKind::RegionOutlives(..) + | ty::PredicateKind::WellFormed(..) + | ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::ClosureKind(..) + | ty::PredicateKind::TypeOutlives(..) + | ty::PredicateKind::ConstEvaluatable(..) + | ty::PredicateKind::ConstEquate(..) + | ty::PredicateKind::TypeWellFormedFromEnv(..) => None, + } + }); + + self.elaborate_bounds(bounds, |this, poly_trait_ref, item| { + 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); + + // Because this trait derives from a where-clause, it + // should not contain any inference variables or other + // artifacts. This means it is safe to put into the + // `WhereClauseCandidate` and (eventually) into the + // `WhereClausePick`. + assert!(!trait_ref.substs.needs_infer()); + + this.push_candidate( + Candidate { + xform_self_ty, + xform_ret_ty, + item, + kind: WhereClauseCandidate(poly_trait_ref), + import_ids: smallvec![], + }, + true, + ); + }); + } + + // Do a search through a list of bounds, using a callback to actually + // create the candidates. + fn elaborate_bounds<F>( + &mut self, + bounds: impl Iterator<Item = ty::PolyTraitRef<'tcx>>, + mut mk_cand: F, + ) where + F: for<'b> FnMut(&mut ProbeContext<'b, 'tcx>, ty::PolyTraitRef<'tcx>, ty::AssocItem), + { + let tcx = self.tcx; + for bound_trait_ref in traits::transitive_bounds(tcx, bounds) { + debug!("elaborate_bounds(bound_trait_ref={:?})", bound_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 { + mk_cand(self, bound_trait_ref, item); + } + } + } + } + + fn assemble_extension_candidates_for_traits_in_scope(&mut self, expr_hir_id: hir::HirId) { + let mut duplicates = FxHashSet::default(); + let opt_applicable_traits = self.tcx.in_scope_traits(expr_hir_id); + if let Some(applicable_traits) = opt_applicable_traits { + for trait_candidate in applicable_traits.iter() { + let trait_did = trait_candidate.def_id; + if duplicates.insert(trait_did) { + self.assemble_extension_candidates_for_trait( + &trait_candidate.import_ids, + trait_did, + ); + } + } + } + } + + fn assemble_extension_candidates_for_all_traits(&mut self) { + let mut duplicates = FxHashSet::default(); + for trait_info in suggest::all_traits(self.tcx) { + if duplicates.insert(trait_info.def_id) { + self.assemble_extension_candidates_for_trait(&smallvec![], trait_info.def_id); + } + } + } + + pub fn matches_return_type( + &self, + 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; + } + } + self.can_sub(self.param_env, fty.output(), expected).is_ok() + }) + } + _ => false, + } + } + + fn assemble_extension_candidates_for_trait( + &mut self, + import_ids: &SmallVec<[LocalDefId; 1]>, + trait_def_id: DefId, + ) { + debug!("assemble_extension_candidates_for_trait(trait_def_id={:?})", trait_def_id); + let trait_substs = self.fresh_item_substs(trait_def_id); + let trait_ref = ty::TraitRef::new(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, + ); + }); + } else { + debug_assert!(self.tcx.is_trait(trait_def_id)); + for item in self.impl_or_trait_item(trait_def_id) { + // Check whether `trait_def_id` defines a method with suitable name. + if !self.has_applicable_self(&item) { + debug!("method has inapplicable self"); + self.record_static_candidate(CandidateSource::Trait(trait_def_id)); + continue; + } + + let (xform_self_ty, xform_ret_ty) = + self.xform_self_ty(&item, trait_ref.self_ty(), trait_substs); + self.push_candidate( + Candidate { + xform_self_ty, + xform_ret_ty, + item, + import_ids: import_ids.clone(), + kind: TraitCandidate(trait_ref), + }, + false, + ); + } + } + } + + fn candidate_method_names(&self) -> Vec<Ident> { + let mut set = FxHashSet::default(); + let mut names: Vec<_> = self + .inherent_candidates + .iter() + .chain(&self.extension_candidates) + .filter(|candidate| { + if let Some(return_ty) = self.return_type { + self.matches_return_type(&candidate.item, None, return_ty) + } else { + true + } + }) + .map(|candidate| candidate.item.ident(self.tcx)) + .filter(|&name| set.insert(name)) + .collect(); + + // Sort them by the name so we have a stable result. + names.sort_by(|a, b| a.as_str().partial_cmp(b.as_str()).unwrap()); + names + } + + /////////////////////////////////////////////////////////////////////////// + // THE ACTUAL SEARCH + + fn pick(mut self) -> PickResult<'tcx> { + assert!(self.method_name.is_some()); + + if let Some(r) = self.pick_core() { + return r; + } + + debug!("pick: actual search failed, assemble diagnostics"); + + let static_candidates = mem::take(&mut self.static_candidates); + let private_candidate = self.private_candidate.take(); + let unsatisfied_predicates = mem::take(&mut self.unsatisfied_predicates); + + // things failed, so lets look at all traits, for diagnostic purposes now: + self.reset(); + + let span = self.span; + let tcx = self.tcx; + + self.assemble_extension_candidates_for_all_traits(); + + let out_of_scope_traits = match self.pick_core() { + Some(Ok(p)) => vec![p.item.container_id(self.tcx)], + //Some(Ok(p)) => p.iter().map(|p| p.item.container().id()).collect(), + Some(Err(MethodError::Ambiguity(v))) => v + .into_iter() + .map(|source| match source { + CandidateSource::Trait(id) => id, + CandidateSource::Impl(impl_id) => match tcx.trait_id_of_impl(impl_id) { + Some(id) => id, + None => span_bug!(span, "found inherent method when looking at traits"), + }, + }) + .collect(), + Some(Err(MethodError::NoMatch(NoMatchData { + out_of_scope_traits: others, .. + }))) => { + assert!(others.is_empty()); + vec![] + } + _ => vec![], + }; + + 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()?; + + Err(MethodError::NoMatch(NoMatchData { + static_candidates, + unsatisfied_predicates, + out_of_scope_traits, + lev_candidate, + mode: self.mode, + })) + } + + fn pick_core(&mut self) -> Option<PickResult<'tcx>> { + let mut unstable_candidates = Vec::new(); + let pick = self.pick_all_method(Some(&mut unstable_candidates)); + + // 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; + } + + match pick { + // Emit a lint if there are unstable candidates alongside the stable ones. + // + // We suppress warning if we're picking the method only because it is a + // suggestion. + Some(Ok(ref p)) if !self.is_suggestion.0 && !unstable_candidates.is_empty() => { + self.emit_unstable_name_collision_hint(p, &unstable_candidates); + pick + } + Some(_) => pick, + None => self.pick_all_method(None), + } + } + + fn pick_all_method( + &mut self, + mut unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, + ) -> Option<PickResult<'tcx>> { + let steps = self.steps.clone(); + steps + .iter() + .filter(|step| { + debug!("pick_all_method: step={:?}", step); + // skip types that are from a type error or that would require dereferencing + // a raw pointer + !step.self_ty.references_error() && !step.from_unsafe_deref + }) + .flat_map(|step| { + let InferOk { value: self_ty, obligations: _ } = self + .fcx + .probe_instantiate_query_response( + self.span, + &self.orig_steps_var_values, + &step.self_ty, + ) + .unwrap_or_else(|_| { + span_bug!(self.span, "{:?} was applicable but now isn't?", step.self_ty) + }); + self.pick_by_value_method(step, self_ty, unstable_candidates.as_deref_mut()) + .or_else(|| { + self.pick_autorefd_method( + step, + self_ty, + hir::Mutability::Not, + unstable_candidates.as_deref_mut(), + ) + .or_else(|| { + self.pick_autorefd_method( + step, + self_ty, + hir::Mutability::Mut, + unstable_candidates.as_deref_mut(), + ) + }) + .or_else(|| { + self.pick_const_ptr_method( + step, + self_ty, + unstable_candidates.as_deref_mut(), + ) + }) + }) + }) + .next() + } + + /// For each type `T` in the step list, this attempts to find a method where + /// the (transformed) self type is exactly `T`. We do however do one + /// transformation on the adjustment: if we are passing a region pointer in, + /// we will potentially *reborrow* it to a shorter lifetime. This allows us + /// to transparently pass `&mut` pointers, in particular, without consuming + /// them for their entire lifetime. + fn pick_by_value_method( + &mut self, + step: &CandidateStep<'tcx>, + self_ty: Ty<'tcx>, + unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, + ) -> Option<PickResult<'tcx>> { + if step.unsize { + return None; + } + + self.pick_method(self_ty, unstable_candidates).map(|r| { + r.map(|mut pick| { + pick.autoderefs = step.autoderefs; + + // Insert a `&*` or `&mut *` if this is a reference type: + if let ty::Ref(_, _, mutbl) = *step.self_ty.value.value.kind() { + pick.autoderefs += 1; + pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::Autoref { + mutbl, + unsize: pick.autoref_or_ptr_adjustment.map_or(false, |a| a.get_unsize()), + }) + } + + pick + }) + }) + } + + fn pick_autorefd_method( + &mut self, + step: &CandidateStep<'tcx>, + self_ty: Ty<'tcx>, + mutbl: hir::Mutability, + unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, + ) -> Option<PickResult<'tcx>> { + let tcx = self.tcx; + + // In general, during probing we erase regions. + let region = tcx.lifetimes.re_erased; + + let autoref_ty = tcx.mk_ref(region, ty::TypeAndMut { ty: self_ty, mutbl }); + self.pick_method(autoref_ty, unstable_candidates).map(|r| { + r.map(|mut pick| { + pick.autoderefs = step.autoderefs; + pick.autoref_or_ptr_adjustment = + Some(AutorefOrPtrAdjustment::Autoref { mutbl, unsize: step.unsize }); + pick + }) + }) + } + + /// If `self_ty` is `*mut T` then this picks `*const T` methods. The reason why we have a + /// special case for this is because going from `*mut T` to `*const T` with autoderefs and + /// autorefs would require dereferencing the pointer, which is not safe. + fn pick_const_ptr_method( + &mut self, + step: &CandidateStep<'tcx>, + self_ty: Ty<'tcx>, + unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, + ) -> Option<PickResult<'tcx>> { + // Don't convert an unsized reference to ptr + if step.unsize { + return None; + } + + let &ty::RawPtr(ty::TypeAndMut { ty, mutbl: hir::Mutability::Mut }) = self_ty.kind() else { + return None; + }; + + let const_self_ty = ty::TypeAndMut { ty, mutbl: hir::Mutability::Not }; + let const_ptr_ty = self.tcx.mk_ptr(const_self_ty); + self.pick_method(const_ptr_ty, unstable_candidates).map(|r| { + r.map(|mut pick| { + pick.autoderefs = step.autoderefs; + pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::ToConstPtr); + pick + }) + }) + } + + fn pick_method_with_unstable(&mut 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(); + let mut unstable_candidates = 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.iter(), + &mut possibly_unsatisfied_predicates, + Some(&mut unstable_candidates), + ); + if let Some(pick) = res { + if !self.is_suggestion.0 && !unstable_candidates.is_empty() { + if let Ok(p) = &pick { + // Emit a lint if there are unstable candidates alongside the stable ones. + // + // We suppress warning if we're picking the method only because it is a + // suggestion. + self.emit_unstable_name_collision_hint(p, &unstable_candidates); + } + } + return Some(pick); + } + } + + debug!("searching unstable candidates"); + let res = self.consider_candidates( + self_ty, + unstable_candidates.iter().map(|(c, _)| c), + &mut possibly_unsatisfied_predicates, + None, + ); + if res.is_none() { + self.unsatisfied_predicates.extend(possibly_unsatisfied_predicates); + } + res + } + + fn pick_method( + &mut 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(); + + for (kind, candidates) in + &[("inherent", &self.inherent_candidates), ("extension", &self.extension_candidates)] + { + debug!("searching {} candidates", kind); + let res = self.consider_candidates( + self_ty, + candidates.iter(), + &mut possibly_unsatisfied_predicates, + unstable_candidates.as_deref_mut(), + ); + if let Some(pick) = res { + return Some(pick); + } + } + + // `pick_method` may be called twice for the same self_ty if no stable methods + // match. Only extend once. + if unstable_candidates.is_some() { + self.unsatisfied_predicates.extend(possibly_unsatisfied_predicates); + } + None + } + + fn consider_candidates<'b, ProbesIter>( + &self, + self_ty: Ty<'tcx>, + probes: ProbesIter, + possibly_unsatisfied_predicates: &mut Vec<( + ty::Predicate<'tcx>, + Option<ty::Predicate<'tcx>>, + Option<ObligationCause<'tcx>>, + )>, + unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, + ) -> Option<PickResult<'tcx>> + where + ProbesIter: Iterator<Item = &'b Candidate<'tcx>> + Clone, + 'tcx: 'b, + { + let mut applicable_candidates: Vec<_> = probes + .clone() + .map(|probe| { + (probe, self.consider_probe(self_ty, probe, possibly_unsatisfied_predicates)) + }) + .filter(|&(_, status)| status != ProbeResult::NoMatch) + .collect(); + + debug!("applicable_candidates: {:?}", applicable_candidates); + + if applicable_candidates.len() > 1 { + if let Some(pick) = + self.collapse_candidates_to_trait_pick(self_ty, &applicable_candidates) + { + return Some(Ok(pick)); + } + } + + if let Some(uc) = unstable_candidates { + applicable_candidates.retain(|&(p, _)| { + if let stability::EvalResult::Deny { feature, .. } = + self.tcx.eval_stability(p.item.def_id, None, self.span, None) + { + uc.push((p.clone(), feature)); + return false; + } + true + }); + } + + if applicable_candidates.len() > 1 { + let sources = probes.map(|p| self.candidate_source(p, self_ty)).collect(); + return Some(Err(MethodError::Ambiguity(sources))); + } + + applicable_candidates.pop().map(|(probe, status)| { + if status == ProbeResult::Match { + Ok(probe.to_unadjusted_pick(self_ty)) + } else { + Err(MethodError::BadReturnType) + } + }) + } + + fn emit_unstable_name_collision_hint( + &self, + stable_pick: &Pick<'_>, + unstable_candidates: &[(Candidate<'tcx>, Symbol)], + ) { + self.tcx.struct_span_lint_hir( + lint::builtin::UNSTABLE_NAME_COLLISIONS, + self.scope_expr_id, + self.span, + |lint| { + let def_kind = stable_pick.item.kind.as_def_kind(); + let mut diag = lint.build(&format!( + "{} {} with this name may be added to the standard library in the future", + def_kind.article(), + def_kind.descr(stable_pick.item.def_id), + )); + match (stable_pick.item.kind, stable_pick.item.container) { + (ty::AssocKind::Fn, _) => { + // FIXME: This should be a `span_suggestion` instead of `help` + // However `self.span` only + // highlights the method name, so we can't use it. Also consider reusing + // the code from `report_method_error()`. + diag.help(&format!( + "call with fully qualified syntax `{}(...)` to keep using the current \ + method", + self.tcx.def_path_str(stable_pick.item.def_id), + )); + } + (ty::AssocKind::Const, ty::AssocItemContainer::TraitContainer) => { + let def_id = stable_pick.item.container_id(self.tcx); + diag.span_suggestion( + self.span, + "use the fully qualified path to the associated const", + format!( + "<{} as {}>::{}", + stable_pick.self_ty, + self.tcx.def_path_str(def_id), + stable_pick.item.name + ), + Applicability::MachineApplicable, + ); + } + _ => {} + } + if self.tcx.sess.is_nightly_build() { + for (candidate, feature) in unstable_candidates { + diag.help(&format!( + "add `#![feature({})]` to the crate attributes to enable `{}`", + feature, + self.tcx.def_path_str(candidate.item.def_id), + )); + } + } + + diag.emit(); + }, + ); + } + + fn select_trait_candidate( + &self, + trait_ref: ty::TraitRef<'tcx>, + ) -> traits::SelectionResult<'tcx, traits::Selection<'tcx>> { + let cause = traits::ObligationCause::misc(self.span, self.body_id); + let predicate = ty::Binder::dummy(trait_ref).to_poly_trait_predicate(); + let obligation = traits::Obligation::new(cause, self.param_env, predicate); + traits::SelectionContext::new(self).select(&obligation) + } + + fn candidate_source(&self, candidate: &Candidate<'tcx>, self_ty: Ty<'tcx>) -> CandidateSource { + match candidate.kind { + InherentImplCandidate(..) => { + CandidateSource::Impl(candidate.item.container_id(self.tcx)) + } + ObjectCandidate | WhereClauseCandidate(_) => { + CandidateSource::Trait(candidate.item.container_id(self.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))) => { + // If only a single impl matches, make the error message point + // to that impl. + CandidateSource::Impl(impl_data.impl_def_id) + } + _ => CandidateSource::Trait(candidate.item.container_id(self.tcx)), + } + }), + } + } + + fn consider_probe( + &self, + self_ty: Ty<'tcx>, + probe: &Candidate<'tcx>, + possibly_unsatisfied_predicates: &mut Vec<( + ty::Predicate<'tcx>, + Option<ty::Predicate<'tcx>>, + Option<ObligationCause<'tcx>>, + )>, + ) -> ProbeResult { + debug!("consider_probe: self_ty={:?} probe={:?}", self_ty, probe); + + self.probe(|_| { + // 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, + Err(err) => { + debug!("--> cannot relate self-types {:?}", err); + return ProbeResult::NoMatch; + } + }; + + let mut result = ProbeResult::Match; + let mut xform_ret_ty = probe.xform_ret_ty; + debug!(?xform_ret_ty); + + let selcx = &mut traits::SelectionContext::new(self); + let cause = traits::ObligationCause::misc(self.span, self.body_id); + + let mut parent_pred = None; + + // If so, impls may carry other conditions (e.g., where + // clauses) that must be considered. Make sure that those + // match as well (or at least may match, sometimes we + // don't have enough information to fully evaluate). + match probe.kind { + InherentImplCandidate(ref substs, ref ref_obligations) => { + // `xform_ret_ty` hasn't been normalized yet, only `xform_self_ty`, + // see the reasons mentioned in the comments in `assemble_inherent_impl_probe` + // for why this is necessary + let traits::Normalized { + value: normalized_xform_ret_ty, + obligations: normalization_obligations, + } = traits::normalize(selcx, self.param_env, cause.clone(), probe.xform_ret_ty); + xform_ret_ty = normalized_xform_ret_ty; + debug!("xform_ret_ty after normalization: {:?}", xform_ret_ty); + + // Check whether the impl imposes obligations we have to worry about. + let impl_def_id = probe.item.container_id(self.tcx); + let impl_bounds = self.tcx.predicates_of(impl_def_id); + let impl_bounds = impl_bounds.instantiate(self.tcx, substs); + let traits::Normalized { value: impl_bounds, obligations: norm_obligations } = + traits::normalize(selcx, self.param_env, cause.clone(), impl_bounds); + + // Convert the bounds into obligations. + let impl_obligations = + traits::predicates_for_generics(cause, self.param_env, impl_bounds); + + let candidate_obligations = impl_obligations + .chain(norm_obligations.into_iter()) + .chain(ref_obligations.iter().cloned()) + .chain(normalization_obligations.into_iter()); + + // Evaluate those obligations to see if they might possibly hold. + for o in candidate_obligations { + let o = self.resolve_vars_if_possible(o); + if !self.predicate_may_hold(&o) { + result = ProbeResult::NoMatch; + possibly_unsatisfied_predicates.push(( + o.predicate, + None, + Some(o.cause), + )); + } + } + } + + ObjectCandidate | WhereClauseCandidate(..) => { + // These have no additional conditions to check. + } + + TraitCandidate(trait_ref) => { + if let Some(method_name) = self.method_name { + // Some trait methods are excluded for arrays before 2021. + // (`array.into_iter()` wants a slice iterator for compatibility.) + if self_ty.is_array() && !method_name.span.rust_2021() { + let trait_def = self.tcx.trait_def(trait_ref.def_id); + if trait_def.skip_array_during_method_dispatch { + return ProbeResult::NoMatch; + } + } + } + let predicate = + ty::Binder::dummy(trait_ref).without_const().to_predicate(self.tcx); + parent_pred = Some(predicate); + let obligation = traits::Obligation::new(cause, self.param_env, predicate); + if !self.predicate_may_hold(&obligation) { + result = ProbeResult::NoMatch; + if self.probe(|_| { + match self.select_trait_candidate(trait_ref) { + Err(_) => return true, + Ok(Some(impl_source)) + if !impl_source.borrow_nested_obligations().is_empty() => + { + for obligation in impl_source.borrow_nested_obligations() { + // Determine exactly which obligation wasn't met, so + // that we can give more context in the error. + if !self.predicate_may_hold(obligation) { + let nested_predicate = + self.resolve_vars_if_possible(obligation.predicate); + let predicate = + self.resolve_vars_if_possible(predicate); + let p = if predicate == nested_predicate { + // Avoid "`MyStruct: Foo` which is required by + // `MyStruct: Foo`" in E0599. + None + } else { + Some(predicate) + }; + possibly_unsatisfied_predicates.push(( + nested_predicate, + p, + Some(obligation.cause.clone()), + )); + } + } + } + _ => { + // Some nested subobligation of this predicate + // failed. + let predicate = self.resolve_vars_if_possible(predicate); + possibly_unsatisfied_predicates.push((predicate, None, None)); + } + } + false + }) { + // This candidate's primary obligation doesn't even + // select - don't bother registering anything in + // `potentially_unsatisfied_predicates`. + return ProbeResult::NoMatch; + } + } + } + } + + // Evaluate those obligations to see if they might possibly hold. + for o in sub_obligations { + let o = self.resolve_vars_if_possible(o); + if !self.predicate_may_hold(&o) { + result = ProbeResult::NoMatch; + possibly_unsatisfied_predicates.push((o.predicate, parent_pred, Some(o.cause))); + } + } + + if let ProbeResult::Match = result { + if let (Some(return_ty), Some(xform_ret_ty)) = (self.return_type, xform_ret_ty) { + let xform_ret_ty = self.resolve_vars_if_possible(xform_ret_ty); + debug!( + "comparing return_ty {:?} with xform ret ty {:?}", + return_ty, probe.xform_ret_ty + ); + if self + .at(&ObligationCause::dummy(), self.param_env) + .define_opaque_types(false) + .sup(return_ty, xform_ret_ty) + .is_err() + { + return ProbeResult::BadReturnType; + } + } + } + + result + }) + } + + /// Sometimes we get in a situation where we have multiple probes that are all impls of the + /// same trait, but we don't know which impl to use. In this case, since in all cases the + /// external interface of the method can be determined from the trait, it's ok not to decide. + /// We can basically just collapse all of the probes for various impls into one where-clause + /// probe. This will result in a pending obligation so when more type-info is available we can + /// make the final decision. + /// + /// Example (`src/test/ui/method-two-trait-defer-resolution-1.rs`): + /// + /// ```ignore (illustrative) + /// trait Foo { ... } + /// impl Foo for Vec<i32> { ... } + /// impl Foo for Vec<usize> { ... } + /// ``` + /// + /// Now imagine the receiver is `Vec<_>`. It doesn't really matter at this time which impl we + /// use, so it's ok to just commit to "using the method from the trait Foo". + fn collapse_candidates_to_trait_pick( + &self, + self_ty: Ty<'tcx>, + probes: &[(&Candidate<'tcx>, ProbeResult)], + ) -> Option<Pick<'tcx>> { + // Do all probes correspond to the same trait? + let container = probes[0].0.item.trait_container(self.tcx)?; + for (p, _) in &probes[1..] { + let p_container = p.item.trait_container(self.tcx)?; + if p_container != container { + return None; + } + } + + // FIXME: check the return type here somehow. + // If so, just use this trait and call it a day. + Some(Pick { + item: probes[0].0.item, + kind: TraitPick, + import_ids: probes[0].0.import_ids.clone(), + autoderefs: 0, + autoref_or_ptr_adjustment: None, + self_ty, + }) + } + + /// 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>> { + debug!("probing for method names similar to {:?}", self.method_name); + + let steps = self.steps.clone(); + self.probe(|_| { + let mut pcx = ProbeContext::new( + self.fcx, + self.span, + self.mode, + self.method_name, + self.return_type, + self.orig_steps_var_values.clone(), + steps, + IsSuggestion(true), + self.scope_expr_id, + ); + pcx.allow_similar_names = true; + pcx.assemble_inherent_candidates(); + + let method_names = pcx.candidate_method_names(); + pcx.allow_similar_names = false; + let applicable_close_candidates: Vec<ty::AssocItem> = method_names + .iter() + .filter_map(|&method_name| { + pcx.reset(); + pcx.method_name = Some(method_name); + pcx.assemble_inherent_candidates(); + pcx.pick_core().and_then(|pick| pick.ok()).map(|pick| pick.item) + }) + .collect(); + + if applicable_close_candidates.is_empty() { + Ok(None) + } else { + let best_name = { + let names = applicable_close_candidates + .iter() + .map(|cand| cand.name) + .collect::<Vec<Symbol>>(); + find_best_match_for_name_with_substrings( + &names, + self.method_name.unwrap().name, + None, + ) + } + .unwrap(); + Ok(applicable_close_candidates.into_iter().find(|method| method.name == best_name)) + } + }) + } + + /////////////////////////////////////////////////////////////////////////// + // MISCELLANY + fn has_applicable_self(&self, item: &ty::AssocItem) -> bool { + // "Fast track" -- check for usage of sugar when in method call + // mode. + // + // In Path mode (i.e., resolving a value like `T::next`), consider any + // associated value (i.e., methods, constants) but not types. + match self.mode { + Mode::MethodCall => item.fn_has_self_parameter, + Mode::Path => match item.kind { + ty::AssocKind::Type => false, + ty::AssocKind::Fn | ty::AssocKind::Const => true, + }, + } + // FIXME -- check for types that deref to `Self`, + // like `Rc<Self>` and so on. + // + // Note also that the current code will break if this type + // includes any of the type parameters defined on the method + // -- but this could be overcome. + } + + fn record_static_candidate(&mut self, source: CandidateSource) { + self.static_candidates.push(source); + } + + #[instrument(level = "debug", skip(self))] + fn xform_self_ty( + &self, + item: &ty::AssocItem, + impl_ty: Ty<'tcx>, + substs: SubstsRef<'tcx>, + ) -> (Ty<'tcx>, Option<Ty<'tcx>>) { + if item.kind == ty::AssocKind::Fn && self.mode == Mode::MethodCall { + let sig = self.xform_method_sig(item.def_id, substs); + (sig.inputs()[0], Some(sig.output())) + } else { + (impl_ty, None) + } + } + + #[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); + debug!(?fn_sig); + + assert!(!substs.has_escaping_bound_vars()); + + // It is possible for type parameters or early-bound lifetimes + // to appear in the signature of `self`. The substitutions we + // are given do not include type/lifetime parameters for the + // method yet. So create fresh variables here for those too, + // if there are any. + let generics = self.tcx.generics_of(method); + assert_eq!(substs.len(), generics.parent_count as usize); + + let xform_fn_sig = if generics.params.is_empty() { + fn_sig.subst(self.tcx, substs) + } else { + let substs = InternalSubsts::for_item(self.tcx, method, |param, _| { + let i = param.index as usize; + if i < substs.len() { + substs[i] + } else { + match param.kind { + GenericParamDefKind::Lifetime => { + // In general, during probe we erase regions. + self.tcx.lifetimes.re_erased.into() + } + GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => { + self.var_for_def(self.span, param) + } + } + } + }); + fn_sig.subst(self.tcx, substs) + }; + + self.erase_late_bound_regions(xform_fn_sig) + } + + /// Gets the type of an impl and generate substitutions with inference vars. + fn impl_ty_and_substs( + &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() + } + }) + } + + /// Replaces late-bound-regions bound by `value` with `'static` using + /// `ty::erase_late_bound_regions`. + /// + /// This is only a reasonable thing to do during the *probe* phase, not the *confirm* phase, of + /// method matching. It is reasonable during the probe phase because we don't consider region + /// relationships at all. Therefore, we can just replace all the region variables with 'static + /// rather than creating fresh region variables. This is nice for two reasons: + /// + /// 1. Because the numbers of the region variables would otherwise be fairly unique to this + /// particular method call, it winds up creating fewer types overall, which helps for memory + /// usage. (Admittedly, this is a rather small effect, though measurable.) + /// + /// 2. It makes it easier to deal with higher-ranked trait bounds, because we can replace any + /// late-bound regions with 'static. Otherwise, if we were going to replace late-bound + /// regions with actual region variables as is proper, we'd have to ensure that the same + /// region got replaced with the same variable, which requires a bit more coordination + /// and/or tracking the substitution and + /// so forth. + fn erase_late_bound_regions<T>(&self, value: ty::Binder<'tcx, T>) -> T + where + T: TypeFoldable<'tcx>, + { + self.tcx.erase_late_bound_regions(value) + } + + /// 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 + // method is fairly hot. + fn impl_or_trait_item(&self, def_id: DefId) -> SmallVec<[ty::AssocItem; 1]> { + if let Some(name) = self.method_name { + if self.allow_similar_names { + let max_dist = max(name.as_str().len(), 3) / 3; + self.tcx + .associated_items(def_id) + .in_definition_order() + .filter(|x| { + if x.kind.namespace() != Namespace::ValueNS { + return false; + } + match lev_distance_with_substrings(name.as_str(), x.name.as_str(), max_dist) + { + Some(d) => d > 0, + None => false, + } + }) + .copied() + .collect() + } else { + self.fcx + .associated_value(def_id, name) + .map_or_else(SmallVec::new, |x| SmallVec::from_buf([x])) + } + } else { + self.tcx.associated_items(def_id).in_definition_order().copied().collect() + } + } +} + +impl<'tcx> Candidate<'tcx> { + fn to_unadjusted_pick(&self, self_ty: Ty<'tcx>) -> Pick<'tcx> { + Pick { + item: self.item, + kind: match self.kind { + InherentImplCandidate(..) => InherentImplPick, + ObjectCandidate => ObjectPick, + TraitCandidate(_) => TraitPick, + WhereClauseCandidate(ref trait_ref) => { + // Only trait derived from where-clauses should + // appear here, so they should not contain any + // inference variables or other artifacts. This + // means they are safe to put into the + // `WhereClausePick`. + assert!( + !trait_ref.skip_binder().substs.needs_infer() + && !trait_ref.skip_binder().substs.has_placeholders() + ); + + WhereClausePick(*trait_ref) + } + }, + import_ids: self.import_ids.clone(), + autoderefs: 0, + autoref_or_ptr_adjustment: None, + self_ty, + } + } +} diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs new file mode 100644 index 000000000..c92b93cbc --- /dev/null +++ b/compiler/rustc_typeck/src/check/method/suggest.rs @@ -0,0 +1,2286 @@ +//! Give useful errors and suggestions to users when an item can't be +//! found or is otherwise invalid. + +use crate::check::FnCtxt; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_errors::{ + pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, + MultiSpan, +}; +use rustc_hir as hir; +use rustc_hir::def::DefKind; +use rustc_hir::def_id::DefId; +use rustc_hir::lang_items::LangItem; +use rustc_hir::{ExprKind, Node, QPath}; +use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_middle::traits::util::supertraits; +use rustc_middle::ty::fast_reject::{simplify_type, TreatParams}; +use rustc_middle::ty::print::with_crate_prefix; +use rustc_middle::ty::ToPolyTraitRef; +use rustc_middle::ty::{self, DefIdTree, ToPredicate, Ty, TyCtxt, TypeVisitable}; +use rustc_span::symbol::{kw, sym, Ident}; +use rustc_span::Symbol; +use rustc_span::{lev_distance, source_map, ExpnKind, FileName, MacroKind, Span}; +use rustc_trait_selection::traits::error_reporting::on_unimplemented::InferCtxtExt as _; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; +use rustc_trait_selection::traits::{ + FulfillmentError, Obligation, ObligationCause, ObligationCauseCode, OnUnimplementedNote, +}; + +use std::cmp::Ordering; +use std::iter; + +use super::probe::{Mode, ProbeScope}; +use super::{super::suggest_call_constructor, CandidateSource, MethodError, NoMatchData}; + +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool { + let tcx = self.tcx; + match ty.kind() { + // Not all of these (e.g., unsafe fns) implement `FnOnce`, + // so we look for these beforehand. + ty::Closure(..) | ty::FnDef(..) | ty::FnPtr(_) => true, + // If it's not a simple function, look for things which implement `FnOnce`. + _ => { + let Some(fn_once) = tcx.lang_items().fn_once_trait() else { + return false; + }; + + // This conditional prevents us from asking to call errors and unresolved types. + // It might seem that we can use `predicate_must_hold_modulo_regions`, + // but since a Dummy binder is used to fill in the FnOnce trait's arguments, + // type resolution always gives a "maybe" here. + if self.autoderef(span, ty).any(|(ty, _)| { + info!("check deref {:?} error", ty); + matches!(ty.kind(), ty::Error(_) | ty::Infer(_)) + }) { + return false; + } + + self.autoderef(span, ty).any(|(ty, _)| { + info!("check deref {:?} impl FnOnce", ty); + self.probe(|_| { + let fn_once_substs = tcx.mk_substs_trait( + ty, + &[self + .next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span, + }) + .into()], + ); + let trait_ref = ty::TraitRef::new(fn_once, fn_once_substs); + let poly_trait_ref = ty::Binder::dummy(trait_ref); + let obligation = Obligation::misc( + span, + self.body_id, + self.param_env, + poly_trait_ref.without_const().to_predicate(tcx), + ); + self.predicate_may_hold(&obligation) + }) + }) + } + } + } + + fn is_slice_ty(&self, ty: Ty<'tcx>, span: Span) -> bool { + self.autoderef(span, ty).any(|(ty, _)| matches!(ty.kind(), ty::Slice(..) | ty::Array(..))) + } + + pub fn report_method_error( + &self, + mut span: Span, + rcvr_ty: Ty<'tcx>, + item_name: Ident, + source: SelfSource<'tcx>, + error: MethodError<'tcx>, + args: Option<&'tcx [hir::Expr<'tcx>]>, + ) -> Option<DiagnosticBuilder<'_, ErrorGuaranteed>> { + // Avoid suggestions when we don't know what's going on. + if rcvr_ty.references_error() { + return None; + } + + let report_candidates = |span: Span, + err: &mut Diagnostic, + mut sources: Vec<CandidateSource>, + sugg_span: Span| { + sources.sort(); + sources.dedup(); + // Dynamic limit to avoid hiding just one candidate, which is silly. + let limit = if sources.len() == 5 { 5 } else { 4 }; + + for (idx, source) in sources.iter().take(limit).enumerate() { + match *source { + CandidateSource::Impl(impl_did) => { + // Provide the best span we can. Use the item, if local to crate, else + // the impl, if local to crate (item may be defaulted), else nothing. + let Some(item) = self.associated_value(impl_did, item_name).or_else(|| { + let impl_trait_ref = self.tcx.impl_trait_ref(impl_did)?; + self.associated_value(impl_trait_ref.def_id, item_name) + }) else { + continue; + }; + + let note_span = if item.def_id.is_local() { + Some(self.tcx.def_span(item.def_id)) + } else if impl_did.is_local() { + Some(self.tcx.def_span(impl_did)) + } else { + None + }; + + let impl_ty = self.tcx.at(span).type_of(impl_did); + + let insertion = match self.tcx.impl_trait_ref(impl_did) { + None => String::new(), + Some(trait_ref) => format!( + " of the trait `{}`", + self.tcx.def_path_str(trait_ref.def_id) + ), + }; + + let (note_str, idx) = if sources.len() > 1 { + ( + format!( + "candidate #{} is defined in an impl{} for the type `{}`", + idx + 1, + insertion, + impl_ty, + ), + Some(idx + 1), + ) + } else { + ( + format!( + "the candidate is defined in an impl{} for the type `{}`", + insertion, impl_ty, + ), + None, + ) + }; + if let Some(note_span) = note_span { + // We have a span pointing to the method. Show note with snippet. + err.span_note(note_span, ¬e_str); + } else { + err.note(¬e_str); + } + if let Some(trait_ref) = self.tcx.impl_trait_ref(impl_did) { + let path = self.tcx.def_path_str(trait_ref.def_id); + + let ty = match item.kind { + ty::AssocKind::Const | ty::AssocKind::Type => rcvr_ty, + ty::AssocKind::Fn => self + .tcx + .fn_sig(item.def_id) + .inputs() + .skip_binder() + .get(0) + .filter(|ty| ty.is_region_ptr() && !rcvr_ty.is_region_ptr()) + .copied() + .unwrap_or(rcvr_ty), + }; + print_disambiguation_help( + item_name, + args, + err, + path, + ty, + item.kind, + item.def_id, + sugg_span, + idx, + self.tcx.sess.source_map(), + item.fn_has_self_parameter, + ); + } + } + CandidateSource::Trait(trait_did) => { + let Some(item) = self.associated_value(trait_did, item_name) else { continue }; + let item_span = self.tcx.def_span(item.def_id); + let idx = if sources.len() > 1 { + let msg = &format!( + "candidate #{} is defined in the trait `{}`", + idx + 1, + self.tcx.def_path_str(trait_did) + ); + err.span_note(item_span, msg); + Some(idx + 1) + } else { + let msg = &format!( + "the candidate is defined in the trait `{}`", + self.tcx.def_path_str(trait_did) + ); + err.span_note(item_span, msg); + None + }; + let path = self.tcx.def_path_str(trait_did); + print_disambiguation_help( + item_name, + args, + err, + path, + rcvr_ty, + item.kind, + item.def_id, + sugg_span, + idx, + self.tcx.sess.source_map(), + item.fn_has_self_parameter, + ); + } + } + } + if sources.len() > limit { + err.note(&format!("and {} others", sources.len() - limit)); + } + }; + + let sugg_span = if let SelfSource::MethodCall(expr) = source { + // Given `foo.bar(baz)`, `expr` is `bar`, but we want to point to the whole thing. + self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(expr.hir_id)).span + } else { + span + }; + + match error { + MethodError::NoMatch(NoMatchData { + static_candidates: static_sources, + unsatisfied_predicates, + out_of_scope_traits, + lev_candidate, + mode, + }) => { + let tcx = self.tcx; + + let actual = self.resolve_vars_if_possible(rcvr_ty); + let ty_str = self.ty_to_string(actual); + let is_method = mode == Mode::MethodCall; + let item_kind = if is_method { + "method" + } else if actual.is_enum() { + "variant or associated item" + } else { + match (item_name.as_str().chars().next(), actual.is_fresh_ty()) { + (Some(name), false) if name.is_lowercase() => "function or associated item", + (Some(_), false) => "associated item", + (Some(_), true) | (None, false) => "variant or associated item", + (None, true) => "variant", + } + }; + + if self.suggest_constraining_numerical_ty( + tcx, actual, source, span, item_kind, item_name, &ty_str, + ) { + return None; + } + + span = item_name.span; + + // Don't show generic arguments when the method can't be found in any implementation (#81576). + let mut ty_str_reported = ty_str.clone(); + if let ty::Adt(_, generics) = actual.kind() { + if generics.len() > 0 { + let mut autoderef = self.autoderef(span, actual); + let candidate_found = autoderef.any(|(ty, _)| { + if let ty::Adt(adt_deref, _) = ty.kind() { + self.tcx + .inherent_impls(adt_deref.did()) + .iter() + .filter_map(|def_id| self.associated_value(*def_id, item_name)) + .count() + >= 1 + } else { + false + } + }); + let has_deref = autoderef.step_count() > 0; + if !candidate_found && !has_deref && unsatisfied_predicates.is_empty() { + if let Some((path_string, _)) = ty_str.split_once('<') { + ty_str_reported = path_string.to_string(); + } + } + } + } + + let mut err = struct_span_err!( + tcx.sess, + span, + E0599, + "no {} named `{}` found for {} `{}` in the current scope", + item_kind, + item_name, + actual.prefix_string(self.tcx), + ty_str_reported, + ); + if actual.references_error() { + err.downgrade_to_delayed_bug(); + } + + if let Mode::MethodCall = mode && let SelfSource::MethodCall(cal) = source { + self.suggest_await_before_method( + &mut err, item_name, actual, cal, span, + ); + } + if let Some(span) = tcx.resolutions(()).confused_type_with_std_module.get(&span) { + err.span_suggestion( + span.shrink_to_lo(), + "you are looking for the module in `std`, not the primitive type", + "std::", + Applicability::MachineApplicable, + ); + } + if let ty::RawPtr(_) = &actual.kind() { + err.note( + "try using `<*const T>::as_ref()` to get a reference to the \ + type behind the pointer: https://doc.rust-lang.org/std/\ + primitive.pointer.html#method.as_ref", + ); + err.note( + "using `<*const T>::as_ref()` on a pointer which is unaligned or points \ + to invalid or uninitialized memory is undefined behavior", + ); + } + + let ty_span = match actual.kind() { + ty::Param(param_type) => { + let generics = self.tcx.generics_of(self.body_id.owner.to_def_id()); + let type_param = generics.type_param(param_type, self.tcx); + Some(self.tcx.def_span(type_param.def_id)) + } + ty::Adt(def, _) if def.did().is_local() => Some(tcx.def_span(def.did())), + _ => None, + }; + + if let Some(span) = ty_span { + err.span_label( + span, + format!( + "{item_kind} `{item_name}` not found for this {}", + actual.prefix_string(self.tcx) + ), + ); + } + + if self.is_fn_ty(rcvr_ty, span) { + if let SelfSource::MethodCall(expr) = source { + let suggest = if let ty::FnDef(def_id, _) = rcvr_ty.kind() { + if let Some(local_id) = def_id.as_local() { + let hir_id = tcx.hir().local_def_id_to_hir_id(local_id); + let node = tcx.hir().get(hir_id); + let fields = node.tuple_fields(); + if let Some(fields) = fields + && let Some(DefKind::Ctor(of, _)) = self.tcx.opt_def_kind(local_id) { + Some((fields.len(), of)) + } else { + None + } + } else { + // The logic here isn't smart but `associated_item_def_ids` + // doesn't work nicely on local. + if let DefKind::Ctor(of, _) = tcx.def_kind(def_id) { + let parent_def_id = tcx.parent(*def_id); + Some((tcx.associated_item_def_ids(parent_def_id).len(), of)) + } else { + None + } + } + } else { + None + }; + + // If the function is a tuple constructor, we recommend that they call it + if let Some((fields, kind)) = suggest { + suggest_call_constructor(expr.span, kind, fields, &mut err); + } else { + // General case + err.span_label( + expr.span, + "this is a function, perhaps you wish to call it", + ); + } + } + } + + let mut custom_span_label = false; + + if !static_sources.is_empty() { + err.note( + "found the following associated functions; to be used as methods, \ + functions must have a `self` parameter", + ); + err.span_label(span, "this is an associated function, not a method"); + custom_span_label = true; + } + if static_sources.len() == 1 { + let ty_str = + if let Some(CandidateSource::Impl(impl_did)) = static_sources.get(0) { + // When the "method" is resolved through dereferencing, we really want the + // original type that has the associated function for accurate suggestions. + // (#61411) + let ty = tcx.at(span).type_of(*impl_did); + match (&ty.peel_refs().kind(), &actual.peel_refs().kind()) { + (ty::Adt(def, _), ty::Adt(def_actual, _)) if def == def_actual => { + // Use `actual` as it will have more `substs` filled in. + self.ty_to_value_string(actual.peel_refs()) + } + _ => self.ty_to_value_string(ty.peel_refs()), + } + } else { + self.ty_to_value_string(actual.peel_refs()) + }; + if let SelfSource::MethodCall(expr) = source { + err.span_suggestion( + expr.span.to(span), + "use associated function syntax instead", + format!("{}::{}", ty_str, item_name), + Applicability::MachineApplicable, + ); + } else { + err.help(&format!("try with `{}::{}`", ty_str, item_name,)); + } + + report_candidates(span, &mut err, static_sources, sugg_span); + } else if static_sources.len() > 1 { + report_candidates(span, &mut err, static_sources, sugg_span); + } + + let mut bound_spans = vec![]; + let mut restrict_type_params = false; + let mut unsatisfied_bounds = false; + if item_name.name == sym::count && self.is_slice_ty(actual, span) { + let msg = "consider using `len` instead"; + if let SelfSource::MethodCall(_expr) = source { + err.span_suggestion_short( + span, + msg, + "len", + Applicability::MachineApplicable, + ); + } else { + err.span_label(span, msg); + } + if let Some(iterator_trait) = self.tcx.get_diagnostic_item(sym::Iterator) { + let iterator_trait = self.tcx.def_path_str(iterator_trait); + err.note(&format!("`count` is defined on `{iterator_trait}`, which `{actual}` does not implement")); + } + } else if !unsatisfied_predicates.is_empty() { + let mut type_params = FxHashMap::default(); + + // Pick out the list of unimplemented traits on the receiver. + // This is used for custom error messages with the `#[rustc_on_unimplemented]` attribute. + let mut unimplemented_traits = FxHashMap::default(); + let mut unimplemented_traits_only = true; + for (predicate, _parent_pred, cause) in &unsatisfied_predicates { + if let (ty::PredicateKind::Trait(p), Some(cause)) = + (predicate.kind().skip_binder(), cause.as_ref()) + { + if p.trait_ref.self_ty() != rcvr_ty { + // This is necessary, not just to keep the errors clean, but also + // because our derived obligations can wind up with a trait ref that + // requires a different param_env to be correctly compared. + continue; + } + unimplemented_traits.entry(p.trait_ref.def_id).or_insert(( + predicate.kind().rebind(p.trait_ref), + Obligation { + cause: cause.clone(), + param_env: self.param_env, + predicate: *predicate, + recursion_depth: 0, + }, + )); + } + } + + // Make sure that, if any traits other than the found ones were involved, + // we don't don't report an unimplemented trait. + // We don't want to say that `iter::Cloned` is not an iterator, just + // because of some non-Clone item being iterated over. + for (predicate, _parent_pred, _cause) in &unsatisfied_predicates { + match predicate.kind().skip_binder() { + ty::PredicateKind::Trait(p) + if unimplemented_traits.contains_key(&p.trait_ref.def_id) => {} + _ => { + unimplemented_traits_only = false; + break; + } + } + } + + let mut collect_type_param_suggestions = + |self_ty: Ty<'tcx>, parent_pred: ty::Predicate<'tcx>, obligation: &str| { + // We don't care about regions here, so it's fine to skip the binder here. + if let (ty::Param(_), ty::PredicateKind::Trait(p)) = + (self_ty.kind(), parent_pred.kind().skip_binder()) + { + let node = match p.trait_ref.self_ty().kind() { + ty::Param(_) => { + // Account for `fn` items like in `issue-35677.rs` to + // suggest restricting its type params. + let did = self.tcx.hir().body_owner_def_id(hir::BodyId { + hir_id: self.body_id, + }); + Some( + self.tcx + .hir() + .get(self.tcx.hir().local_def_id_to_hir_id(did)), + ) + } + ty::Adt(def, _) => def.did().as_local().map(|def_id| { + self.tcx + .hir() + .get(self.tcx.hir().local_def_id_to_hir_id(def_id)) + }), + _ => None, + }; + if let Some(hir::Node::Item(hir::Item { kind, .. })) = node { + if let Some(g) = kind.generics() { + let key = ( + g.tail_span_for_predicate_suggestion(), + g.add_where_or_trailing_comma(), + ); + type_params + .entry(key) + .or_insert_with(FxHashSet::default) + .insert(obligation.to_owned()); + } + } + } + }; + let mut bound_span_label = |self_ty: Ty<'_>, obligation: &str, quiet: &str| { + let msg = format!( + "doesn't satisfy `{}`", + if obligation.len() > 50 { quiet } else { obligation } + ); + match &self_ty.kind() { + // Point at the type that couldn't satisfy the bound. + ty::Adt(def, _) => { + bound_spans.push((self.tcx.def_span(def.did()), msg)) + } + // Point at the trait object that couldn't satisfy the bound. + ty::Dynamic(preds, _) => { + for pred in preds.iter() { + match pred.skip_binder() { + ty::ExistentialPredicate::Trait(tr) => bound_spans + .push((self.tcx.def_span(tr.def_id), msg.clone())), + ty::ExistentialPredicate::Projection(_) + | ty::ExistentialPredicate::AutoTrait(_) => {} + } + } + } + // Point at the closure that couldn't satisfy the bound. + ty::Closure(def_id, _) => bound_spans.push(( + tcx.def_span(*def_id), + format!("doesn't satisfy `{}`", quiet), + )), + _ => {} + } + }; + let mut format_pred = |pred: ty::Predicate<'tcx>| { + let bound_predicate = pred.kind(); + match bound_predicate.skip_binder() { + ty::PredicateKind::Projection(pred) => { + let pred = bound_predicate.rebind(pred); + // `<Foo as Iterator>::Item = String`. + let projection_ty = pred.skip_binder().projection_ty; + + let substs_with_infer_self = tcx.mk_substs( + iter::once(tcx.mk_ty_var(ty::TyVid::from_u32(0)).into()) + .chain(projection_ty.substs.iter().skip(1)), + ); + + let quiet_projection_ty = ty::ProjectionTy { + substs: substs_with_infer_self, + item_def_id: projection_ty.item_def_id, + }; + + let term = pred.skip_binder().term; + + let obligation = format!("{} = {}", projection_ty, term); + let quiet = format!("{} = {}", quiet_projection_ty, term); + + bound_span_label(projection_ty.self_ty(), &obligation, &quiet); + Some((obligation, projection_ty.self_ty())) + } + ty::PredicateKind::Trait(poly_trait_ref) => { + let p = poly_trait_ref.trait_ref; + let self_ty = p.self_ty(); + let path = p.print_only_trait_path(); + let obligation = format!("{}: {}", self_ty, path); + let quiet = format!("_: {}", path); + bound_span_label(self_ty, &obligation, &quiet); + Some((obligation, self_ty)) + } + _ => None, + } + }; + + // Find all the requirements that come from a local `impl` block. + let mut skip_list: FxHashSet<_> = Default::default(); + let mut spanned_predicates: FxHashMap<MultiSpan, _> = Default::default(); + for (data, p, parent_p, impl_def_id, cause) in unsatisfied_predicates + .iter() + .filter_map(|(p, parent, c)| c.as_ref().map(|c| (p, parent, c))) + .filter_map(|(p, parent, c)| match c.code() { + ObligationCauseCode::ImplDerivedObligation(ref data) => { + Some((&data.derived, p, parent, data.impl_def_id, data)) + } + _ => None, + }) + { + let parent_trait_ref = data.parent_trait_pred; + let path = parent_trait_ref.print_modifiers_and_trait_path(); + let tr_self_ty = parent_trait_ref.skip_binder().self_ty(); + let unsatisfied_msg = "unsatisfied trait bound introduced here"; + let derive_msg = + "unsatisfied trait bound introduced in this `derive` macro"; + match self.tcx.hir().get_if_local(impl_def_id) { + // Unmet obligation comes from a `derive` macro, point at it once to + // avoid multiple span labels pointing at the same place. + Some(Node::Item(hir::Item { + kind: hir::ItemKind::Trait(..), + ident, + .. + })) if matches!( + ident.span.ctxt().outer_expn_data().kind, + ExpnKind::Macro(MacroKind::Derive, _) + ) => + { + let span = ident.span.ctxt().outer_expn_data().call_site; + let mut spans: MultiSpan = span.into(); + spans.push_span_label(span, derive_msg); + let entry = spanned_predicates.entry(spans); + entry.or_insert_with(|| (path, tr_self_ty, Vec::new())).2.push(p); + } + + Some(Node::Item(hir::Item { + kind: hir::ItemKind::Impl(hir::Impl { of_trait, self_ty, .. }), + .. + })) if matches!( + self_ty.span.ctxt().outer_expn_data().kind, + ExpnKind::Macro(MacroKind::Derive, _) + ) || matches!( + of_trait.as_ref().map(|t| t + .path + .span + .ctxt() + .outer_expn_data() + .kind), + Some(ExpnKind::Macro(MacroKind::Derive, _)) + ) => + { + let span = self_ty.span.ctxt().outer_expn_data().call_site; + let mut spans: MultiSpan = span.into(); + spans.push_span_label(span, derive_msg); + let entry = spanned_predicates.entry(spans); + entry.or_insert_with(|| (path, tr_self_ty, Vec::new())).2.push(p); + } + + // Unmet obligation coming from a `trait`. + Some(Node::Item(hir::Item { + kind: hir::ItemKind::Trait(..), + ident, + span: item_span, + .. + })) if !matches!( + ident.span.ctxt().outer_expn_data().kind, + ExpnKind::Macro(MacroKind::Derive, _) + ) => + { + if let Some(pred) = parent_p { + // Done to add the "doesn't satisfy" `span_label`. + let _ = format_pred(*pred); + } + skip_list.insert(p); + let mut spans = if cause.span != *item_span { + let mut spans: MultiSpan = cause.span.into(); + spans.push_span_label(cause.span, unsatisfied_msg); + spans + } else { + ident.span.into() + }; + spans.push_span_label(ident.span, "in this trait"); + let entry = spanned_predicates.entry(spans); + entry.or_insert_with(|| (path, tr_self_ty, Vec::new())).2.push(p); + } + + // Unmet obligation coming from an `impl`. + Some(Node::Item(hir::Item { + kind: + hir::ItemKind::Impl(hir::Impl { + of_trait, self_ty, generics, .. + }), + span: item_span, + .. + })) if !matches!( + self_ty.span.ctxt().outer_expn_data().kind, + ExpnKind::Macro(MacroKind::Derive, _) + ) && !matches!( + of_trait.as_ref().map(|t| t + .path + .span + .ctxt() + .outer_expn_data() + .kind), + Some(ExpnKind::Macro(MacroKind::Derive, _)) + ) => + { + let sized_pred = + unsatisfied_predicates.iter().any(|(pred, _, _)| { + match pred.kind().skip_binder() { + ty::PredicateKind::Trait(pred) => { + Some(pred.def_id()) + == self.tcx.lang_items().sized_trait() + && pred.polarity == ty::ImplPolarity::Positive + } + _ => false, + } + }); + for param in generics.params { + if param.span == cause.span && sized_pred { + let (sp, sugg) = match param.colon_span { + Some(sp) => (sp.shrink_to_hi(), " ?Sized +"), + None => (param.span.shrink_to_hi(), ": ?Sized"), + }; + err.span_suggestion_verbose( + sp, + "consider relaxing the type parameter's implicit \ + `Sized` bound", + sugg, + Applicability::MachineApplicable, + ); + } + } + if let Some(pred) = parent_p { + // Done to add the "doesn't satisfy" `span_label`. + let _ = format_pred(*pred); + } + skip_list.insert(p); + let mut spans = if cause.span != *item_span { + let mut spans: MultiSpan = cause.span.into(); + spans.push_span_label(cause.span, unsatisfied_msg); + spans + } else { + let mut spans = Vec::with_capacity(2); + if let Some(trait_ref) = of_trait { + spans.push(trait_ref.path.span); + } + spans.push(self_ty.span); + spans.into() + }; + if let Some(trait_ref) = of_trait { + spans.push_span_label(trait_ref.path.span, ""); + } + spans.push_span_label(self_ty.span, ""); + + let entry = spanned_predicates.entry(spans); + entry.or_insert_with(|| (path, tr_self_ty, Vec::new())).2.push(p); + } + _ => {} + } + } + let mut spanned_predicates: Vec<_> = spanned_predicates.into_iter().collect(); + spanned_predicates.sort_by_key(|(span, (_, _, _))| span.primary_span()); + for (span, (_path, _self_ty, preds)) in spanned_predicates { + let mut preds: Vec<_> = preds + .into_iter() + .filter_map(|pred| format_pred(*pred)) + .map(|(p, _)| format!("`{}`", p)) + .collect(); + preds.sort(); + preds.dedup(); + let msg = if let [pred] = &preds[..] { + format!("trait bound {} was not satisfied", pred) + } else { + format!( + "the following trait bounds were not satisfied:\n{}", + preds.join("\n"), + ) + }; + err.span_note(span, &msg); + unsatisfied_bounds = true; + } + + // The requirements that didn't have an `impl` span to show. + let mut bound_list = unsatisfied_predicates + .iter() + .filter_map(|(pred, parent_pred, _cause)| { + format_pred(*pred).map(|(p, self_ty)| { + collect_type_param_suggestions(self_ty, *pred, &p); + ( + match parent_pred { + None => format!("`{}`", &p), + Some(parent_pred) => match format_pred(*parent_pred) { + None => format!("`{}`", &p), + Some((parent_p, _)) => { + collect_type_param_suggestions( + self_ty, + *parent_pred, + &p, + ); + format!( + "`{}`\nwhich is required by `{}`", + p, parent_p + ) + } + }, + }, + *pred, + ) + }) + }) + .filter(|(_, pred)| !skip_list.contains(&pred)) + .map(|(t, _)| t) + .enumerate() + .collect::<Vec<(usize, String)>>(); + + for ((span, add_where_or_comma), obligations) in type_params.into_iter() { + restrict_type_params = true; + // #74886: Sort here so that the output is always the same. + let mut obligations = obligations.into_iter().collect::<Vec<_>>(); + obligations.sort(); + err.span_suggestion_verbose( + span, + &format!( + "consider restricting the type parameter{s} to satisfy the \ + trait bound{s}", + s = pluralize!(obligations.len()) + ), + format!("{} {}", add_where_or_comma, obligations.join(", ")), + Applicability::MaybeIncorrect, + ); + } + + bound_list.sort_by(|(_, a), (_, b)| a.cmp(b)); // Sort alphabetically. + bound_list.dedup_by(|(_, a), (_, b)| a == b); // #35677 + bound_list.sort_by_key(|(pos, _)| *pos); // Keep the original predicate order. + + if !bound_list.is_empty() || !skip_list.is_empty() { + let bound_list = bound_list + .into_iter() + .map(|(_, path)| path) + .collect::<Vec<_>>() + .join("\n"); + let actual_prefix = actual.prefix_string(self.tcx); + info!("unimplemented_traits.len() == {}", unimplemented_traits.len()); + let (primary_message, label) = + if unimplemented_traits.len() == 1 && unimplemented_traits_only { + unimplemented_traits + .into_iter() + .next() + .map(|(_, (trait_ref, obligation))| { + if trait_ref.self_ty().references_error() + || actual.references_error() + { + // Avoid crashing. + return (None, None); + } + let OnUnimplementedNote { message, label, .. } = + self.on_unimplemented_note(trait_ref, &obligation); + (message, label) + }) + .unwrap_or((None, None)) + } else { + (None, None) + }; + let primary_message = primary_message.unwrap_or_else(|| format!( + "the {item_kind} `{item_name}` exists for {actual_prefix} `{ty_str}`, but its trait bounds were not satisfied" + )); + err.set_primary_message(&primary_message); + if let Some(label) = label { + custom_span_label = true; + err.span_label(span, label); + } + if !bound_list.is_empty() { + err.note(&format!( + "the following trait bounds were not satisfied:\n{bound_list}" + )); + } + self.suggest_derive(&mut err, &unsatisfied_predicates); + + unsatisfied_bounds = true; + } + } + + let label_span_not_found = |err: &mut DiagnosticBuilder<'_, _>| { + if unsatisfied_predicates.is_empty() { + err.span_label(span, format!("{item_kind} not found in `{ty_str}`")); + let is_string_or_ref_str = match actual.kind() { + ty::Ref(_, ty, _) => { + ty.is_str() + || matches!( + ty.kind(), + ty::Adt(adt, _) if self.tcx.is_diagnostic_item(sym::String, adt.did()) + ) + } + ty::Adt(adt, _) => self.tcx.is_diagnostic_item(sym::String, adt.did()), + _ => false, + }; + if is_string_or_ref_str && item_name.name == sym::iter { + err.span_suggestion_verbose( + item_name.span, + "because of the in-memory representation of `&str`, to obtain \ + an `Iterator` over each of its codepoint use method `chars`", + "chars", + Applicability::MachineApplicable, + ); + } + if let ty::Adt(adt, _) = rcvr_ty.kind() { + let mut inherent_impls_candidate = self + .tcx + .inherent_impls(adt.did()) + .iter() + .copied() + .filter(|def_id| { + if let Some(assoc) = self.associated_value(*def_id, item_name) { + // Check for both mode is the same so we avoid suggesting + // incorrect associated item. + match (mode, assoc.fn_has_self_parameter, source) { + (Mode::MethodCall, true, SelfSource::MethodCall(_)) => { + // We check that the suggest type is actually + // different from the received one + // So we avoid suggestion method with Box<Self> + // for instance + self.tcx.at(span).type_of(*def_id) != actual + && self.tcx.at(span).type_of(*def_id) != rcvr_ty + } + (Mode::Path, false, _) => true, + _ => false, + } + } else { + false + } + }) + .collect::<Vec<_>>(); + if !inherent_impls_candidate.is_empty() { + inherent_impls_candidate.sort(); + inherent_impls_candidate.dedup(); + + // number of type to shows at most. + let limit = if inherent_impls_candidate.len() == 5 { 5 } else { 4 }; + let type_candidates = inherent_impls_candidate + .iter() + .take(limit) + .map(|impl_item| { + format!("- `{}`", self.tcx.at(span).type_of(*impl_item)) + }) + .collect::<Vec<_>>() + .join("\n"); + let additional_types = if inherent_impls_candidate.len() > limit { + format!( + "\nand {} more types", + inherent_impls_candidate.len() - limit + ) + } else { + "".to_string() + }; + err.note(&format!( + "the {item_kind} was found for\n{}{}", + type_candidates, additional_types + )); + } + } + } else { + err.span_label(span, format!("{item_kind} cannot be called on `{ty_str}` due to unsatisfied trait bounds")); + } + }; + + // If the method name is the name of a field with a function or closure type, + // give a helping note that it has to be called as `(x.f)(...)`. + if let SelfSource::MethodCall(expr) = source { + if !self.suggest_field_call(span, rcvr_ty, expr, item_name, &mut err) + && lev_candidate.is_none() + && !custom_span_label + { + label_span_not_found(&mut err); + } + } else if !custom_span_label { + label_span_not_found(&mut err); + } + + self.check_for_field_method(&mut err, source, span, actual, item_name); + + self.check_for_unwrap_self(&mut err, source, span, actual, item_name); + + bound_spans.sort(); + bound_spans.dedup(); + for (span, msg) in bound_spans.into_iter() { + err.span_label(span, &msg); + } + + if actual.is_numeric() && actual.is_fresh() || restrict_type_params { + } else { + self.suggest_traits_to_import( + &mut err, + span, + rcvr_ty, + item_name, + args.map(|args| args.len()), + source, + out_of_scope_traits, + &unsatisfied_predicates, + unsatisfied_bounds, + ); + } + + // Don't emit a suggestion if we found an actual method + // that had unsatisfied trait bounds + if unsatisfied_predicates.is_empty() && actual.is_enum() { + let adt_def = actual.ty_adt_def().expect("enum is not an ADT"); + if let Some(suggestion) = lev_distance::find_best_match_for_name( + &adt_def.variants().iter().map(|s| s.name).collect::<Vec<_>>(), + item_name.name, + None, + ) { + err.span_suggestion( + span, + "there is a variant with a similar name", + suggestion, + Applicability::MaybeIncorrect, + ); + } + } + + if item_name.name == sym::as_str && actual.peel_refs().is_str() { + let msg = "remove this method call"; + let mut fallback_span = true; + if let SelfSource::MethodCall(expr) = source { + let call_expr = + self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(expr.hir_id)); + if let Some(span) = call_expr.span.trim_start(expr.span) { + err.span_suggestion(span, msg, "", Applicability::MachineApplicable); + fallback_span = false; + } + } + if fallback_span { + err.span_label(span, msg); + } + } else if let Some(lev_candidate) = lev_candidate { + // Don't emit a suggestion if we found an actual method + // that had unsatisfied trait bounds + if unsatisfied_predicates.is_empty() { + let def_kind = lev_candidate.kind.as_def_kind(); + err.span_suggestion( + span, + &format!( + "there is {} {} with a similar name", + def_kind.article(), + def_kind.descr(lev_candidate.def_id), + ), + lev_candidate.name, + Applicability::MaybeIncorrect, + ); + } + } + + return Some(err); + } + + MethodError::Ambiguity(sources) => { + let mut err = struct_span_err!( + self.sess(), + item_name.span, + E0034, + "multiple applicable items in scope" + ); + err.span_label(item_name.span, format!("multiple `{}` found", item_name)); + + report_candidates(span, &mut err, sources, sugg_span); + err.emit(); + } + + MethodError::PrivateMatch(kind, def_id, out_of_scope_traits) => { + let kind = kind.descr(def_id); + let mut err = struct_span_err!( + self.tcx.sess, + item_name.span, + E0624, + "{} `{}` is private", + kind, + item_name + ); + err.span_label(item_name.span, &format!("private {}", kind)); + let sp = self + .tcx + .hir() + .span_if_local(def_id) + .unwrap_or_else(|| self.tcx.def_span(def_id)); + err.span_label(sp, &format!("private {} defined here", kind)); + self.suggest_valid_traits(&mut err, out_of_scope_traits); + err.emit(); + } + + MethodError::IllegalSizedBound(candidates, needs_mut, bound_span) => { + let msg = format!("the `{}` method cannot be invoked on a trait object", item_name); + let mut err = self.sess().struct_span_err(span, &msg); + err.span_label(bound_span, "this has a `Sized` requirement"); + if !candidates.is_empty() { + let help = format!( + "{an}other candidate{s} {were} found in the following trait{s}, perhaps \ + add a `use` for {one_of_them}:", + an = if candidates.len() == 1 { "an" } else { "" }, + s = pluralize!(candidates.len()), + were = pluralize!("was", candidates.len()), + one_of_them = if candidates.len() == 1 { "it" } else { "one_of_them" }, + ); + self.suggest_use_candidates(&mut err, help, candidates); + } + if let ty::Ref(region, t_type, mutability) = rcvr_ty.kind() { + if needs_mut { + let trait_type = self.tcx.mk_ref( + *region, + ty::TypeAndMut { ty: *t_type, mutbl: mutability.invert() }, + ); + err.note(&format!("you need `{}` instead of `{}`", trait_type, rcvr_ty)); + } + } + err.emit(); + } + + MethodError::BadReturnType => bug!("no return type expectations but got BadReturnType"), + } + None + } + + fn suggest_field_call( + &self, + span: Span, + rcvr_ty: Ty<'tcx>, + expr: &hir::Expr<'_>, + item_name: Ident, + err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>, + ) -> bool { + let tcx = self.tcx; + let field_receiver = self.autoderef(span, rcvr_ty).find_map(|(ty, _)| match ty.kind() { + ty::Adt(def, substs) if !def.is_enum() => { + let variant = &def.non_enum_variant(); + tcx.find_field_index(item_name, variant).map(|index| { + let field = &variant.fields[index]; + let field_ty = field.ty(tcx, substs); + (field, field_ty) + }) + } + _ => None, + }); + if let Some((field, field_ty)) = field_receiver { + let scope = tcx.parent_module(self.body_id).to_def_id(); + let is_accessible = field.vis.is_accessible_from(scope, tcx); + + if is_accessible { + if self.is_fn_ty(field_ty, span) { + let expr_span = expr.span.to(item_name.span); + err.multipart_suggestion( + &format!( + "to call the function stored in `{}`, \ + surround the field access with parentheses", + item_name, + ), + vec![ + (expr_span.shrink_to_lo(), '('.to_string()), + (expr_span.shrink_to_hi(), ')'.to_string()), + ], + Applicability::MachineApplicable, + ); + } else { + let call_expr = tcx.hir().expect_expr(tcx.hir().get_parent_node(expr.hir_id)); + + if let Some(span) = call_expr.span.trim_start(item_name.span) { + err.span_suggestion( + span, + "remove the arguments", + "", + Applicability::MaybeIncorrect, + ); + } + } + } + + let field_kind = if is_accessible { "field" } else { "private field" }; + err.span_label(item_name.span, format!("{}, not a method", field_kind)); + return true; + } + false + } + + fn suggest_constraining_numerical_ty( + &self, + tcx: TyCtxt<'tcx>, + actual: Ty<'tcx>, + source: SelfSource<'_>, + span: Span, + item_kind: &str, + item_name: Ident, + ty_str: &str, + ) -> bool { + let found_candidate = all_traits(self.tcx) + .into_iter() + .any(|info| self.associated_value(info.def_id, item_name).is_some()); + let found_assoc = |ty: Ty<'tcx>| { + simplify_type(tcx, ty, TreatParams::AsInfer) + .and_then(|simp| { + tcx.incoherent_impls(simp) + .iter() + .find_map(|&id| self.associated_value(id, item_name)) + }) + .is_some() + }; + let found_candidate = found_candidate + || found_assoc(tcx.types.i8) + || found_assoc(tcx.types.i16) + || found_assoc(tcx.types.i32) + || found_assoc(tcx.types.i64) + || found_assoc(tcx.types.i128) + || found_assoc(tcx.types.u8) + || found_assoc(tcx.types.u16) + || found_assoc(tcx.types.u32) + || found_assoc(tcx.types.u64) + || found_assoc(tcx.types.u128) + || found_assoc(tcx.types.f32) + || found_assoc(tcx.types.f32); + if found_candidate + && actual.is_numeric() + && !actual.has_concrete_skeleton() + && let SelfSource::MethodCall(expr) = source + { + let mut err = struct_span_err!( + tcx.sess, + span, + E0689, + "can't call {} `{}` on ambiguous numeric type `{}`", + item_kind, + item_name, + ty_str + ); + let concrete_type = if actual.is_integral() { "i32" } else { "f32" }; + match expr.kind { + ExprKind::Lit(ref lit) => { + // numeric literal + let snippet = tcx + .sess + .source_map() + .span_to_snippet(lit.span) + .unwrap_or_else(|_| "<numeric literal>".to_owned()); + + // If this is a floating point literal that ends with '.', + // get rid of it to stop this from becoming a member access. + let snippet = snippet.strip_suffix('.').unwrap_or(&snippet); + + err.span_suggestion( + lit.span, + &format!( + "you must specify a concrete type for this numeric value, \ + like `{}`", + concrete_type + ), + format!("{snippet}_{concrete_type}"), + Applicability::MaybeIncorrect, + ); + } + ExprKind::Path(QPath::Resolved(_, path)) => { + // local binding + if let hir::def::Res::Local(hir_id) = path.res { + let span = tcx.hir().span(hir_id); + let snippet = tcx.sess.source_map().span_to_snippet(span); + let filename = tcx.sess.source_map().span_to_filename(span); + + let parent_node = + self.tcx.hir().get(self.tcx.hir().get_parent_node(hir_id)); + let msg = format!( + "you must specify a type for this binding, like `{}`", + concrete_type, + ); + + match (filename, parent_node, snippet) { + ( + FileName::Real(_), + Node::Local(hir::Local { + source: hir::LocalSource::Normal, + ty, + .. + }), + Ok(ref snippet), + ) => { + err.span_suggestion( + // account for `let x: _ = 42;` + // ^^^^ + span.to(ty.as_ref().map(|ty| ty.span).unwrap_or(span)), + &msg, + format!("{}: {}", snippet, concrete_type), + Applicability::MaybeIncorrect, + ); + } + _ => { + err.span_label(span, msg); + } + } + } + } + _ => {} + } + err.emit(); + return true; + } + false + } + + fn check_for_field_method( + &self, + err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>, + source: SelfSource<'tcx>, + span: Span, + actual: Ty<'tcx>, + item_name: Ident, + ) { + if let SelfSource::MethodCall(expr) = source + && let Some((fields, substs)) = self.get_field_candidates(span, actual) + { + let call_expr = self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(expr.hir_id)); + for candidate_field in fields.iter() { + if let Some(field_path) = self.check_for_nested_field_satisfying( + span, + &|_, field_ty| { + self.lookup_probe( + span, + item_name, + field_ty, + call_expr, + ProbeScope::AllTraits, + ) + .is_ok() + }, + candidate_field, + substs, + vec![], + self.tcx.parent_module(expr.hir_id).to_def_id(), + ) { + let field_path_str = field_path + .iter() + .map(|id| id.name.to_ident_string()) + .collect::<Vec<String>>() + .join("."); + debug!("field_path_str: {:?}", field_path_str); + + err.span_suggestion_verbose( + item_name.span.shrink_to_lo(), + "one of the expressions' fields has a method of the same name", + format!("{field_path_str}."), + Applicability::MaybeIncorrect, + ); + } + } + } + } + + fn check_for_unwrap_self( + &self, + err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>, + source: SelfSource<'tcx>, + span: Span, + actual: Ty<'tcx>, + item_name: Ident, + ) { + let tcx = self.tcx; + let SelfSource::MethodCall(expr) = source else { return; }; + let call_expr = tcx.hir().expect_expr(tcx.hir().get_parent_node(expr.hir_id)); + + let ty::Adt(kind, substs) = actual.kind() else { return; }; + if !kind.is_enum() { + return; + } + + let matching_variants: Vec<_> = kind + .variants() + .iter() + .flat_map(|variant| { + let [field] = &variant.fields[..] else { return None; }; + let field_ty = field.ty(tcx, substs); + + // Skip `_`, since that'll just lead to ambiguity. + if self.resolve_vars_if_possible(field_ty).is_ty_var() { + return None; + } + + self.lookup_probe(span, item_name, field_ty, call_expr, ProbeScope::AllTraits) + .ok() + .map(|pick| (variant, field, pick)) + }) + .collect(); + + let ret_ty_matches = |diagnostic_item| { + if let Some(ret_ty) = self + .ret_coercion + .as_ref() + .map(|c| self.resolve_vars_if_possible(c.borrow().expected_ty())) + && let ty::Adt(kind, _) = ret_ty.kind() + && tcx.get_diagnostic_item(diagnostic_item) == Some(kind.did()) + { + true + } else { + false + } + }; + + match &matching_variants[..] { + [(_, field, pick)] => { + let self_ty = field.ty(tcx, substs); + err.span_note( + tcx.def_span(pick.item.def_id), + &format!("the method `{item_name}` exists on the type `{self_ty}`"), + ); + let (article, kind, variant, question) = + if Some(kind.did()) == tcx.get_diagnostic_item(sym::Result) { + ("a", "Result", "Err", ret_ty_matches(sym::Result)) + } else if Some(kind.did()) == tcx.get_diagnostic_item(sym::Option) { + ("an", "Option", "None", ret_ty_matches(sym::Option)) + } else { + return; + }; + if question { + err.span_suggestion_verbose( + expr.span.shrink_to_hi(), + format!( + "use the `?` operator to extract the `{self_ty}` value, propagating \ + {article} `{kind}::{variant}` value to the caller" + ), + "?", + Applicability::MachineApplicable, + ); + } else { + err.span_suggestion_verbose( + expr.span.shrink_to_hi(), + format!( + "consider using `{kind}::expect` to unwrap the `{self_ty}` value, \ + panicking if the value is {article} `{kind}::{variant}`" + ), + ".expect(\"REASON\")", + Applicability::HasPlaceholders, + ); + } + } + // FIXME(compiler-errors): Support suggestions for other matching enum variants + _ => {} + } + } + + pub(crate) fn note_unmet_impls_on_type( + &self, + err: &mut Diagnostic, + errors: Vec<FulfillmentError<'tcx>>, + ) { + let all_local_types_needing_impls = + errors.iter().all(|e| match e.obligation.predicate.kind().skip_binder() { + ty::PredicateKind::Trait(pred) => match pred.self_ty().kind() { + ty::Adt(def, _) => def.did().is_local(), + _ => false, + }, + _ => false, + }); + let mut preds: Vec<_> = errors + .iter() + .filter_map(|e| match e.obligation.predicate.kind().skip_binder() { + ty::PredicateKind::Trait(pred) => Some(pred), + _ => None, + }) + .collect(); + preds.sort_by_key(|pred| (pred.def_id(), pred.self_ty())); + let def_ids = preds + .iter() + .filter_map(|pred| match pred.self_ty().kind() { + ty::Adt(def, _) => Some(def.did()), + _ => None, + }) + .collect::<FxHashSet<_>>(); + let mut spans: MultiSpan = def_ids + .iter() + .filter_map(|def_id| { + let span = self.tcx.def_span(*def_id); + if span.is_dummy() { None } else { Some(span) } + }) + .collect::<Vec<_>>() + .into(); + + for pred in &preds { + match pred.self_ty().kind() { + ty::Adt(def, _) if def.did().is_local() => { + spans.push_span_label( + self.tcx.def_span(def.did()), + format!("must implement `{}`", pred.trait_ref.print_only_trait_path()), + ); + } + _ => {} + } + } + + if all_local_types_needing_impls && spans.primary_span().is_some() { + let msg = if preds.len() == 1 { + format!( + "an implementation of `{}` might be missing for `{}`", + preds[0].trait_ref.print_only_trait_path(), + preds[0].self_ty() + ) + } else { + format!( + "the following type{} would have to `impl` {} required trait{} for this \ + operation to be valid", + pluralize!(def_ids.len()), + if def_ids.len() == 1 { "its" } else { "their" }, + pluralize!(preds.len()), + ) + }; + err.span_note(spans, &msg); + } + + let preds: Vec<_> = errors + .iter() + .map(|e| (e.obligation.predicate, None, Some(e.obligation.cause.clone()))) + .collect(); + self.suggest_derive(err, &preds); + } + + fn suggest_derive( + &self, + err: &mut Diagnostic, + unsatisfied_predicates: &[( + ty::Predicate<'tcx>, + Option<ty::Predicate<'tcx>>, + Option<ObligationCause<'tcx>>, + )], + ) { + let mut derives = Vec::<(String, Span, Symbol)>::new(); + let mut traits = Vec::<Span>::new(); + for (pred, _, _) in unsatisfied_predicates { + let ty::PredicateKind::Trait(trait_pred) = pred.kind().skip_binder() else { continue }; + let adt = match trait_pred.self_ty().ty_adt_def() { + Some(adt) if adt.did().is_local() => adt, + _ => continue, + }; + if let Some(diagnostic_name) = self.tcx.get_diagnostic_name(trait_pred.def_id()) { + let can_derive = match diagnostic_name { + sym::Default => !adt.is_enum(), + sym::Eq + | sym::PartialEq + | sym::Ord + | sym::PartialOrd + | sym::Clone + | sym::Copy + | sym::Hash + | sym::Debug => true, + _ => false, + }; + if can_derive { + let self_name = trait_pred.self_ty().to_string(); + let self_span = self.tcx.def_span(adt.did()); + if let Some(poly_trait_ref) = pred.to_opt_poly_trait_pred() { + for super_trait in supertraits(self.tcx, poly_trait_ref.to_poly_trait_ref()) + { + if let Some(parent_diagnostic_name) = + self.tcx.get_diagnostic_name(super_trait.def_id()) + { + derives.push(( + self_name.clone(), + self_span, + parent_diagnostic_name, + )); + } + } + } + derives.push((self_name, self_span, diagnostic_name)); + } else { + traits.push(self.tcx.def_span(trait_pred.def_id())); + } + } else { + traits.push(self.tcx.def_span(trait_pred.def_id())); + } + } + traits.sort(); + traits.dedup(); + + derives.sort(); + derives.dedup(); + + let mut derives_grouped = Vec::<(String, Span, String)>::new(); + for (self_name, self_span, trait_name) in derives.into_iter() { + if let Some((last_self_name, _, ref mut last_trait_names)) = derives_grouped.last_mut() + { + if last_self_name == &self_name { + last_trait_names.push_str(format!(", {}", trait_name).as_str()); + continue; + } + } + derives_grouped.push((self_name, self_span, trait_name.to_string())); + } + + let len = traits.len(); + if len > 0 { + let span: MultiSpan = traits.into(); + err.span_note( + span, + &format!("the following trait{} must be implemented", pluralize!(len),), + ); + } + + for (self_name, self_span, traits) in &derives_grouped { + err.span_suggestion_verbose( + self_span.shrink_to_lo(), + &format!("consider annotating `{}` with `#[derive({})]`", self_name, traits), + format!("#[derive({})]\n", traits), + Applicability::MaybeIncorrect, + ); + } + } + + /// Print out the type for use in value namespace. + fn ty_to_value_string(&self, ty: Ty<'tcx>) -> String { + match ty.kind() { + ty::Adt(def, substs) => format!("{}", ty::Instance::new(def.did(), substs)), + _ => self.ty_to_string(ty), + } + } + + fn suggest_await_before_method( + &self, + err: &mut Diagnostic, + item_name: Ident, + ty: Ty<'tcx>, + call: &hir::Expr<'_>, + span: Span, + ) { + let output_ty = match self.get_impl_future_output_ty(ty) { + Some(output_ty) => self.resolve_vars_if_possible(output_ty).skip_binder(), + _ => return, + }; + let method_exists = self.method_exists(item_name, output_ty, call.hir_id, true); + debug!("suggest_await_before_method: is_method_exist={}", method_exists); + if method_exists { + err.span_suggestion_verbose( + span.shrink_to_lo(), + "consider `await`ing on the `Future` and calling the method on its `Output`", + "await.", + Applicability::MaybeIncorrect, + ); + } + } + + fn suggest_use_candidates(&self, err: &mut Diagnostic, msg: String, candidates: Vec<DefId>) { + let parent_map = self.tcx.visible_parent_map(()); + + // Separate out candidates that must be imported with a glob, because they are named `_` + // and cannot be referred with their identifier. + let (candidates, globs): (Vec<_>, Vec<_>) = candidates.into_iter().partition(|trait_did| { + if let Some(parent_did) = parent_map.get(trait_did) { + // If the item is re-exported as `_`, we should suggest a glob-import instead. + if *parent_did != self.tcx.parent(*trait_did) + && self + .tcx + .module_children(*parent_did) + .iter() + .filter(|child| child.res.opt_def_id() == Some(*trait_did)) + .all(|child| child.ident.name == kw::Underscore) + { + return false; + } + } + + true + }); + + let module_did = self.tcx.parent_module(self.body_id); + let (module, _, _) = self.tcx.hir().get_module(module_did); + let span = module.spans.inject_use_span; + + let path_strings = candidates.iter().map(|trait_did| { + format!("use {};\n", with_crate_prefix!(self.tcx.def_path_str(*trait_did)),) + }); + + let glob_path_strings = globs.iter().map(|trait_did| { + let parent_did = parent_map.get(trait_did).unwrap(); + format!( + "use {}::*; // trait {}\n", + with_crate_prefix!(self.tcx.def_path_str(*parent_did)), + self.tcx.item_name(*trait_did), + ) + }); + + err.span_suggestions( + span, + &msg, + path_strings.chain(glob_path_strings), + Applicability::MaybeIncorrect, + ); + } + + fn suggest_valid_traits( + &self, + err: &mut Diagnostic, + valid_out_of_scope_traits: Vec<DefId>, + ) -> bool { + if !valid_out_of_scope_traits.is_empty() { + let mut candidates = valid_out_of_scope_traits; + candidates.sort(); + candidates.dedup(); + + // `TryFrom` and `FromIterator` have no methods + let edition_fix = candidates + .iter() + .find(|did| self.tcx.is_diagnostic_item(sym::TryInto, **did)) + .copied(); + + err.help("items from traits can only be used if the trait is in scope"); + let msg = format!( + "the following {traits_are} implemented but not in scope; \ + perhaps add a `use` for {one_of_them}:", + traits_are = if candidates.len() == 1 { "trait is" } else { "traits are" }, + one_of_them = if candidates.len() == 1 { "it" } else { "one of them" }, + ); + + self.suggest_use_candidates(err, msg, candidates); + if let Some(did) = edition_fix { + err.note(&format!( + "'{}' is included in the prelude starting in Edition 2021", + with_crate_prefix!(self.tcx.def_path_str(did)) + )); + } + + true + } else { + false + } + } + + fn suggest_traits_to_import( + &self, + err: &mut Diagnostic, + span: Span, + rcvr_ty: Ty<'tcx>, + item_name: Ident, + inputs_len: Option<usize>, + source: SelfSource<'tcx>, + valid_out_of_scope_traits: Vec<DefId>, + unsatisfied_predicates: &[( + ty::Predicate<'tcx>, + Option<ty::Predicate<'tcx>>, + Option<ObligationCause<'tcx>>, + )], + unsatisfied_bounds: bool, + ) { + let mut alt_rcvr_sugg = false; + if let (SelfSource::MethodCall(rcvr), false) = (source, unsatisfied_bounds) { + debug!(?span, ?item_name, ?rcvr_ty, ?rcvr); + let skippable = [ + self.tcx.lang_items().clone_trait(), + self.tcx.lang_items().deref_trait(), + self.tcx.lang_items().deref_mut_trait(), + self.tcx.lang_items().drop_trait(), + self.tcx.get_diagnostic_item(sym::AsRef), + ]; + // Try alternative arbitrary self types that could fulfill this call. + // FIXME: probe for all types that *could* be arbitrary self-types, not + // just this list. + for (rcvr_ty, post) in &[ + (rcvr_ty, ""), + (self.tcx.mk_mut_ref(self.tcx.lifetimes.re_erased, rcvr_ty), "&mut "), + (self.tcx.mk_imm_ref(self.tcx.lifetimes.re_erased, rcvr_ty), "&"), + ] { + match self.lookup_probe(span, item_name, *rcvr_ty, rcvr, ProbeScope::AllTraits) { + Ok(pick) => { + // If the method is defined for the receiver we have, it likely wasn't `use`d. + // We point at the method, but we just skip the rest of the check for arbitrary + // self types and rely on the suggestion to `use` the trait from + // `suggest_valid_traits`. + let did = Some(pick.item.container_id(self.tcx)); + let skip = skippable.contains(&did); + if pick.autoderefs == 0 && !skip { + err.span_label( + pick.item.ident(self.tcx).span, + &format!("the method is available for `{}` here", rcvr_ty), + ); + } + break; + } + Err(MethodError::Ambiguity(_)) => { + // If the method is defined (but ambiguous) for the receiver we have, it is also + // likely we haven't `use`d it. It may be possible that if we `Box`/`Pin`/etc. + // the receiver, then it might disambiguate this method, but I think these + // suggestions are generally misleading (see #94218). + break; + } + _ => {} + } + + for (rcvr_ty, pre) in &[ + (self.tcx.mk_lang_item(*rcvr_ty, LangItem::OwnedBox), "Box::new"), + (self.tcx.mk_lang_item(*rcvr_ty, LangItem::Pin), "Pin::new"), + (self.tcx.mk_diagnostic_item(*rcvr_ty, sym::Arc), "Arc::new"), + (self.tcx.mk_diagnostic_item(*rcvr_ty, sym::Rc), "Rc::new"), + ] { + if let Some(new_rcvr_t) = *rcvr_ty + && let Ok(pick) = self.lookup_probe( + span, + item_name, + new_rcvr_t, + rcvr, + ProbeScope::AllTraits, + ) + { + debug!("try_alt_rcvr: pick candidate {:?}", pick); + let did = Some(pick.item.container_id(self.tcx)); + // We don't want to suggest a container type when the missing + // method is `.clone()` or `.deref()` otherwise we'd suggest + // `Arc::new(foo).clone()`, which is far from what the user wants. + // Explicitly ignore the `Pin::as_ref()` method as `Pin` does not + // implement the `AsRef` trait. + let skip = skippable.contains(&did) + || (("Pin::new" == *pre) && (sym::as_ref == item_name.name)) + || inputs_len.map_or(false, |inputs_len| pick.item.kind == ty::AssocKind::Fn && self.tcx.fn_sig(pick.item.def_id).skip_binder().inputs().len() != inputs_len); + // Make sure the method is defined for the *actual* receiver: we don't + // want to treat `Box<Self>` as a receiver if it only works because of + // an autoderef to `&self` + if pick.autoderefs == 0 && !skip { + err.span_label( + pick.item.ident(self.tcx).span, + &format!("the method is available for `{}` here", new_rcvr_t), + ); + err.multipart_suggestion( + "consider wrapping the receiver expression with the \ + appropriate type", + vec![ + (rcvr.span.shrink_to_lo(), format!("{}({}", pre, post)), + (rcvr.span.shrink_to_hi(), ")".to_string()), + ], + Applicability::MaybeIncorrect, + ); + // We don't care about the other suggestions. + alt_rcvr_sugg = true; + } + } + } + } + } + if self.suggest_valid_traits(err, valid_out_of_scope_traits) { + return; + } + + let type_is_local = self.type_derefs_to_local(span, rcvr_ty, source); + + let mut arbitrary_rcvr = vec![]; + // There are no traits implemented, so lets suggest some traits to + // implement, by finding ones that have the item name, and are + // legal to implement. + let mut candidates = all_traits(self.tcx) + .into_iter() + // Don't issue suggestions for unstable traits since they're + // unlikely to be implementable anyway + .filter(|info| match self.tcx.lookup_stability(info.def_id) { + Some(attr) => attr.level.is_stable(), + None => true, + }) + .filter(|info| { + // We approximate the coherence rules to only suggest + // traits that are legal to implement by requiring that + // either the type or trait is local. Multi-dispatch means + // this isn't perfect (that is, there are cases when + // implementing a trait would be legal but is rejected + // here). + unsatisfied_predicates.iter().all(|(p, _, _)| { + match p.kind().skip_binder() { + // Hide traits if they are present in predicates as they can be fixed without + // having to implement them. + ty::PredicateKind::Trait(t) => t.def_id() == info.def_id, + ty::PredicateKind::Projection(p) => { + p.projection_ty.item_def_id == info.def_id + } + _ => false, + } + }) && (type_is_local || info.def_id.is_local()) + && self + .associated_value(info.def_id, item_name) + .filter(|item| { + if let ty::AssocKind::Fn = item.kind { + let id = item + .def_id + .as_local() + .map(|def_id| self.tcx.hir().local_def_id_to_hir_id(def_id)); + if let Some(hir::Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Fn(fn_sig, method), + .. + })) = id.map(|id| self.tcx.hir().get(id)) + { + let self_first_arg = match method { + hir::TraitFn::Required([ident, ..]) => { + ident.name == kw::SelfLower + } + hir::TraitFn::Provided(body_id) => { + self.tcx.hir().body(*body_id).params.first().map_or( + false, + |param| { + matches!( + param.pat.kind, + hir::PatKind::Binding(_, _, ident, _) + if ident.name == kw::SelfLower + ) + }, + ) + } + _ => false, + }; + + if !fn_sig.decl.implicit_self.has_implicit_self() + && self_first_arg + { + if let Some(ty) = fn_sig.decl.inputs.get(0) { + arbitrary_rcvr.push(ty.span); + } + return false; + } + } + } + // We only want to suggest public or local traits (#45781). + item.visibility(self.tcx).is_public() || info.def_id.is_local() + }) + .is_some() + }) + .collect::<Vec<_>>(); + for span in &arbitrary_rcvr { + err.span_label( + *span, + "the method might not be found because of this arbitrary self type", + ); + } + if alt_rcvr_sugg { + return; + } + + if !candidates.is_empty() { + // Sort from most relevant to least relevant. + candidates.sort_by(|a, b| a.cmp(b).reverse()); + candidates.dedup(); + + let param_type = match rcvr_ty.kind() { + ty::Param(param) => Some(param), + ty::Ref(_, ty, _) => match ty.kind() { + ty::Param(param) => Some(param), + _ => None, + }, + _ => None, + }; + err.help(if param_type.is_some() { + "items from traits can only be used if the type parameter is bounded by the trait" + } else { + "items from traits can only be used if the trait is implemented and in scope" + }); + let candidates_len = candidates.len(); + let message = |action| { + format!( + "the following {traits_define} an item `{name}`, perhaps you need to {action} \ + {one_of_them}:", + traits_define = + if candidates_len == 1 { "trait defines" } else { "traits define" }, + action = action, + one_of_them = if candidates_len == 1 { "it" } else { "one of them" }, + name = item_name, + ) + }; + // Obtain the span for `param` and use it for a structured suggestion. + if let Some(param) = param_type { + let generics = self.tcx.generics_of(self.body_id.owner.to_def_id()); + let type_param = generics.type_param(param, self.tcx); + let hir = self.tcx.hir(); + if let Some(def_id) = type_param.def_id.as_local() { + let id = hir.local_def_id_to_hir_id(def_id); + // Get the `hir::Param` to verify whether it already has any bounds. + // We do this to avoid suggesting code that ends up as `T: FooBar`, + // instead we suggest `T: Foo + Bar` in that case. + match hir.get(id) { + Node::GenericParam(param) => { + enum Introducer { + Plus, + Colon, + Nothing, + } + let ast_generics = hir.get_generics(id.owner).unwrap(); + let (sp, mut introducer) = if let Some(span) = + ast_generics.bounds_span_for_suggestions(def_id) + { + (span, Introducer::Plus) + } else if let Some(colon_span) = param.colon_span { + (colon_span.shrink_to_hi(), Introducer::Nothing) + } else { + (param.span.shrink_to_hi(), Introducer::Colon) + }; + if matches!( + param.kind, + hir::GenericParamKind::Type { synthetic: true, .. }, + ) { + introducer = Introducer::Plus + } + let trait_def_ids: FxHashSet<DefId> = ast_generics + .bounds_for_param(def_id) + .flat_map(|bp| bp.bounds.iter()) + .filter_map(|bound| bound.trait_ref()?.trait_def_id()) + .collect(); + if !candidates.iter().any(|t| trait_def_ids.contains(&t.def_id)) { + err.span_suggestions( + sp, + &message(format!( + "restrict type parameter `{}` with", + param.name.ident(), + )), + candidates.iter().map(|t| { + format!( + "{} {}", + match introducer { + Introducer::Plus => " +", + Introducer::Colon => ":", + Introducer::Nothing => "", + }, + self.tcx.def_path_str(t.def_id), + ) + }), + Applicability::MaybeIncorrect, + ); + } + return; + } + Node::Item(hir::Item { + kind: hir::ItemKind::Trait(.., bounds, _), + ident, + .. + }) => { + let (sp, sep, article) = if bounds.is_empty() { + (ident.span.shrink_to_hi(), ":", "a") + } else { + (bounds.last().unwrap().span().shrink_to_hi(), " +", "another") + }; + err.span_suggestions( + sp, + &message(format!("add {} supertrait for", article)), + candidates.iter().map(|t| { + format!("{} {}", sep, self.tcx.def_path_str(t.def_id),) + }), + Applicability::MaybeIncorrect, + ); + return; + } + _ => {} + } + } + } + + let (potential_candidates, explicitly_negative) = if param_type.is_some() { + // FIXME: Even though negative bounds are not implemented, we could maybe handle + // cases where a positive bound implies a negative impl. + (candidates, Vec::new()) + } else if let Some(simp_rcvr_ty) = + simplify_type(self.tcx, rcvr_ty, TreatParams::AsPlaceholder) + { + let mut potential_candidates = Vec::new(); + let mut explicitly_negative = Vec::new(); + for candidate in candidates { + // Check if there's a negative impl of `candidate` for `rcvr_ty` + if self + .tcx + .all_impls(candidate.def_id) + .filter(|imp_did| { + self.tcx.impl_polarity(*imp_did) == ty::ImplPolarity::Negative + }) + .any(|imp_did| { + let imp = self.tcx.impl_trait_ref(imp_did).unwrap(); + let imp_simp = + simplify_type(self.tcx, imp.self_ty(), TreatParams::AsPlaceholder); + imp_simp.map_or(false, |s| s == simp_rcvr_ty) + }) + { + explicitly_negative.push(candidate); + } else { + potential_candidates.push(candidate); + } + } + (potential_candidates, explicitly_negative) + } else { + // We don't know enough about `recv_ty` to make proper suggestions. + (candidates, Vec::new()) + }; + + let action = if let Some(param) = param_type { + format!("restrict type parameter `{}` with", param) + } else { + // FIXME: it might only need to be imported into scope, not implemented. + "implement".to_string() + }; + match &potential_candidates[..] { + [] => {} + [trait_info] if trait_info.def_id.is_local() => { + err.span_note( + self.tcx.def_span(trait_info.def_id), + &format!( + "`{}` defines an item `{}`, perhaps you need to {} it", + self.tcx.def_path_str(trait_info.def_id), + item_name, + action + ), + ); + } + trait_infos => { + let mut msg = message(action); + for (i, trait_info) in trait_infos.iter().enumerate() { + msg.push_str(&format!( + "\ncandidate #{}: `{}`", + i + 1, + self.tcx.def_path_str(trait_info.def_id), + )); + } + err.note(&msg); + } + } + match &explicitly_negative[..] { + [] => {} + [trait_info] => { + let msg = format!( + "the trait `{}` defines an item `{}`, but is explicitly unimplemented", + self.tcx.def_path_str(trait_info.def_id), + item_name + ); + err.note(&msg); + } + trait_infos => { + let mut msg = format!( + "the following traits define an item `{}`, but are explicitly unimplemented:", + item_name + ); + for trait_info in trait_infos { + msg.push_str(&format!("\n{}", self.tcx.def_path_str(trait_info.def_id))); + } + err.note(&msg); + } + } + } + } + + /// Checks whether there is a local type somewhere in the chain of + /// autoderefs of `rcvr_ty`. + fn type_derefs_to_local( + &self, + span: Span, + rcvr_ty: Ty<'tcx>, + source: SelfSource<'tcx>, + ) -> bool { + fn is_local(ty: Ty<'_>) -> bool { + match ty.kind() { + ty::Adt(def, _) => def.did().is_local(), + ty::Foreign(did) => did.is_local(), + ty::Dynamic(tr, ..) => tr.principal().map_or(false, |d| d.def_id().is_local()), + ty::Param(_) => true, + + // Everything else (primitive types, etc.) is effectively + // non-local (there are "edge" cases, e.g., `(LocalType,)`, but + // the noise from these sort of types is usually just really + // annoying, rather than any sort of help). + _ => false, + } + } + + // This occurs for UFCS desugaring of `T::method`, where there is no + // receiver expression for the method call, and thus no autoderef. + if let SelfSource::QPath(_) = source { + return is_local(self.resolve_vars_with_obligations(rcvr_ty)); + } + + self.autoderef(span, rcvr_ty).any(|(ty, _)| is_local(ty)) + } +} + +#[derive(Copy, Clone, Debug)] +pub enum SelfSource<'a> { + QPath(&'a hir::Ty<'a>), + MethodCall(&'a hir::Expr<'a> /* rcvr */), +} + +#[derive(Copy, Clone)] +pub struct TraitInfo { + pub def_id: DefId, +} + +impl PartialEq for TraitInfo { + fn eq(&self, other: &TraitInfo) -> bool { + self.cmp(other) == Ordering::Equal + } +} +impl Eq for TraitInfo {} +impl PartialOrd for TraitInfo { + fn partial_cmp(&self, other: &TraitInfo) -> Option<Ordering> { + Some(self.cmp(other)) + } +} +impl Ord for TraitInfo { + fn cmp(&self, other: &TraitInfo) -> Ordering { + // Local crates are more important than remote ones (local: + // `cnum == 0`), and otherwise we throw in the defid for totality. + + let lhs = (other.def_id.krate, other.def_id); + let rhs = (self.def_id.krate, self.def_id); + lhs.cmp(&rhs) + } +} + +/// Retrieves all traits in this crate and any dependent crates, +/// and wraps them into `TraitInfo` for custom sorting. +pub fn all_traits(tcx: TyCtxt<'_>) -> Vec<TraitInfo> { + tcx.all_traits().map(|def_id| TraitInfo { def_id }).collect() +} + +fn print_disambiguation_help<'tcx>( + item_name: Ident, + args: Option<&'tcx [hir::Expr<'tcx>]>, + err: &mut Diagnostic, + trait_name: String, + rcvr_ty: Ty<'_>, + kind: ty::AssocKind, + def_id: DefId, + span: Span, + candidate: Option<usize>, + source_map: &source_map::SourceMap, + fn_has_self_parameter: bool, +) { + let mut applicability = Applicability::MachineApplicable; + let (span, sugg) = if let (ty::AssocKind::Fn, Some(args)) = (kind, args) { + let args = format!( + "({}{})", + if rcvr_ty.is_region_ptr() { + if rcvr_ty.is_mutable_ptr() { "&mut " } else { "&" } + } else { + "" + }, + args.iter() + .map(|arg| source_map.span_to_snippet(arg.span).unwrap_or_else(|_| { + applicability = Applicability::HasPlaceholders; + "_".to_owned() + })) + .collect::<Vec<_>>() + .join(", "), + ); + let trait_name = if !fn_has_self_parameter { + format!("<{} as {}>", rcvr_ty, trait_name) + } else { + trait_name + }; + (span, format!("{}::{}{}", trait_name, item_name, args)) + } else { + (span.with_hi(item_name.span.lo()), format!("<{} as {}>::", rcvr_ty, trait_name)) + }; + err.span_suggestion_verbose( + span, + &format!( + "disambiguate the {} for {}", + kind.as_def_kind().descr(def_id), + if let Some(candidate) = candidate { + format!("candidate #{}", candidate) + } else { + "the candidate".to_string() + }, + ), + sugg, + applicability, + ); +} |