diff options
Diffstat (limited to 'compiler/rustc_hir_typeck/src/method')
-rw-r--r-- | compiler/rustc_hir_typeck/src/method/confirm.rs | 14 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/method/probe.rs | 102 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/method/suggest.rs | 132 |
3 files changed, 158 insertions, 90 deletions
diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index 169f128e0..9155a3d8d 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -8,7 +8,7 @@ use rustc_hir_analysis::astconv::generics::{ check_generic_arg_count_for_call, create_substs_for_generic_args, }; use rustc_hir_analysis::astconv::{AstConv, CreateSubstsForGenericArgsCtxt, IsMethodCall}; -use rustc_infer::infer::{self, InferOk}; +use rustc_infer::infer::{self, DefineOpaqueTypes, InferOk}; use rustc_middle::traits::{ObligationCauseCode, UnifyReceiverContext}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCast}; use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutability}; @@ -478,7 +478,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { substs, })), ); - match self.at(&cause, self.param_env).sup(method_self_ty, self_ty) { + match self.at(&cause, self.param_env).sup(DefineOpaqueTypes::No, method_self_ty, self_ty) { Ok(InferOk { obligations, value: () }) => { self.register_predicates(obligations); } @@ -574,19 +574,15 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { ) -> Option<Span> { let sized_def_id = self.tcx.lang_items().sized_trait()?; - traits::elaborate_predicates(self.tcx, predicates.predicates.iter().copied()) + traits::elaborate(self.tcx, predicates.predicates.iter().copied()) // We don't care about regions here. - .filter_map(|obligation| match obligation.predicate.kind().skip_binder() { + .filter_map(|pred| match pred.kind().skip_binder() { ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) if trait_pred.def_id() == sized_def_id => { let span = predicates .iter() - .find_map( - |(p, span)| { - if p == obligation.predicate { Some(span) } else { None } - }, - ) + .find_map(|(p, span)| if p == pred { Some(span) } else { None }) .unwrap_or(rustc_span::DUMMY_SP); Some((trait_pred, span)) } diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 3bef5cfcd..4fd778910 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -13,6 +13,7 @@ use rustc_hir_analysis::astconv::InferCtxtExt as _; use rustc_hir_analysis::autoderef::{self, Autoderef}; use rustc_infer::infer::canonical::OriginalQueryValues; use rustc_infer::infer::canonical::{Canonical, QueryResponse}; +use rustc_infer::infer::DefineOpaqueTypes; use rustc_infer::infer::{self, InferOk, TyCtxtInferExt}; use rustc_middle::middle::stability; use rustc_middle::ty::fast_reject::{simplify_type, TreatParams}; @@ -699,7 +700,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } 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 { + let Some(simp) = simplify_type(self.tcx, self_ty, TreatParams::AsCandidateKey) else { bug!("unexpected incoherent type: {:?}", self_ty) }; for &impl_def_id in self.tcx.incoherent_impls(simp) { @@ -792,6 +793,14 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { // 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| { + if new_trait_ref.has_non_region_late_bound() { + this.tcx.sess.delay_span_bug( + this.span, + "tried to select method from HRTB with non-lifetime bound vars", + ); + return; + } + let new_trait_ref = this.erase_late_bound_regions(new_trait_ref); let (xform_self_ty, xform_ret_ty) = @@ -836,24 +845,21 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) | ty::PredicateKind::Ambiguous - | ty::PredicateKind::AliasEq(..) + | ty::PredicateKind::AliasRelate(..) | 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 trait_ref = this.instantiate_binder_with_fresh_vars( + this.span, + infer::LateBoundRegionConversionTime::FnCall, + 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, @@ -929,7 +935,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { if let Some(self_ty) = self_ty { if self .at(&ObligationCause::dummy(), self.param_env) - .sup(fty.inputs()[0], self_ty) + .sup(DefineOpaqueTypes::No, fty.inputs()[0], self_ty) .is_err() { return false; @@ -963,7 +969,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { bound_trait_ref.def_id(), )); } else { - let new_trait_ref = self.erase_late_bound_regions(bound_trait_ref); + let new_trait_ref = self.instantiate_binder_with_fresh_vars( + self.span, + infer::LateBoundRegionConversionTime::FnCall, + bound_trait_ref, + ); let (xform_self_ty, xform_ret_ty) = self.xform_self_ty(item, new_trait_ref.self_ty(), new_trait_ref.substs); @@ -1026,12 +1036,21 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { true } }) + // ensure that we don't suggest unstable methods + .filter(|candidate| { + // note that `DUMMY_SP` is ok here because it is only used for + // suggestions and macro stuff which isn't applicable here. + !matches!( + self.tcx.eval_stability(candidate.item.def_id, None, DUMMY_SP, None), + stability::EvalResult::Deny { .. } + ) + }) .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.sort_by(|a, b| a.as_str().cmp(b.as_str())); names } @@ -1338,6 +1357,7 @@ impl<'tcx> Pick<'tcx> { container: _, trait_item_def_id: _, fn_has_self_parameter: _, + opt_rpitit_info: _, }, kind: _, import_ids: _, @@ -1434,9 +1454,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { CandidateSource::Trait(candidate.item.container_id(self.tcx)) } TraitCandidate(trait_ref) => self.probe(|_| { - let _ = self - .at(&ObligationCause::dummy(), self.param_env) - .sup(candidate.xform_self_ty, self_ty); + let _ = self.at(&ObligationCause::dummy(), self.param_env).sup( + DefineOpaqueTypes::No, + 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 @@ -1463,10 +1485,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self.probe(|_| { // First check that the self type can be related. - let sub_obligations = match self - .at(&ObligationCause::dummy(), self.param_env) - .sup(probe.xform_self_ty, self_ty) - { + let sub_obligations = match self.at(&ObligationCause::dummy(), self.param_env).sup( + DefineOpaqueTypes::No, + probe.xform_self_ty, + self_ty, + ) { Ok(InferOk { obligations, value: () }) => obligations, Err(err) => { debug!("--> cannot relate self-types {:?}", err); @@ -1508,23 +1531,18 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { // Convert the bounds into obligations. let impl_obligations = traits::predicates_for_generics( - |_idx, span| { - let misc = traits::ObligationCause::misc(span, self.body_id); - let parent_trait_pred = ty::Binder::dummy(ty::TraitPredicate { - trait_ref: ty::TraitRef::from_method(self.tcx, impl_def_id, substs), - constness: ty::BoundConstness::NotConst, - polarity: ty::ImplPolarity::Positive, - }); - misc.derived_cause(parent_trait_pred, |derived| { - traits::ImplDerivedObligation(Box::new( - traits::ImplDerivedObligationCause { - derived, - impl_or_alias_def_id: impl_def_id, - impl_def_predicate_index: None, - span, - }, - )) - }) + |idx, span| { + let code = if span.is_dummy() { + traits::ExprItemObligation(impl_def_id, self.scope_expr_id, idx) + } else { + traits::ExprBindingObligation( + impl_def_id, + span, + self.scope_expr_id, + idx, + ) + }; + ObligationCause::new(self.span, self.body_id, code) }, self.param_env, impl_bounds, @@ -1541,8 +1559,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { if !self.predicate_may_hold(&o) { result = ProbeResult::NoMatch; let parent_o = o.clone(); - let implied_obligations = - traits::elaborate_obligations(self.tcx, vec![o]); + let implied_obligations = traits::elaborate(self.tcx, vec![o]); for o in implied_obligations { let parent = if o == parent_o { None @@ -1681,7 +1698,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { if let ProbeResult::Match = result && self .at(&ObligationCause::dummy(), self.param_env) - .sup(return_ty, xform_ret_ty) + .sup(DefineOpaqueTypes::No, return_ty, xform_ret_ty) .is_err() { result = ProbeResult::BadReturnType; @@ -1742,7 +1759,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { fn probe_for_similar_candidate(&mut self) -> Result<Option<ty::AssocItem>, MethodError<'tcx>> { debug!("probing for method names similar to {:?}", self.method_name); - let steps = self.steps.clone(); self.probe(|_| { let mut pcx = ProbeContext::new( self.fcx, @@ -1750,8 +1766,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self.mode, self.method_name, self.return_type, - &self.orig_steps_var_values, - steps, + self.orig_steps_var_values, + self.steps, self.scope_expr_id, ); pcx.allow_similar_names = true; diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 60d56263d..900a6fa0d 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -27,7 +27,7 @@ use rustc_middle::traits::util::supertraits; use rustc_middle::ty::fast_reject::DeepRejectCtxt; use rustc_middle::ty::fast_reject::{simplify_type, TreatParams}; use rustc_middle::ty::print::{with_crate_prefix, with_forced_trimmed_paths}; -use rustc_middle::ty::{self, DefIdTree, GenericArgKind, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{self, GenericArgKind, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::ty::{IsSuggestable, ToPolyTraitRef}; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::Symbol; @@ -42,7 +42,7 @@ use rustc_trait_selection::traits::{ use super::probe::{AutorefOrPtrAdjustment, IsSuggestion, Mode, ProbeScope}; use super::{CandidateSource, MethodError, NoMatchData}; use rustc_hir::intravisit::Visitor; -use std::cmp::Ordering; +use std::cmp::{self, Ordering}; use std::iter; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -245,6 +245,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None } + fn suggest_missing_writer( + &self, + rcvr_ty: Ty<'tcx>, + args: (&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>]), + ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { + let (ty_str, _ty_file) = self.tcx.short_ty_string(rcvr_ty); + let mut err = + struct_span_err!(self.tcx.sess, args.0.span, E0599, "cannot write into `{}`", ty_str); + err.span_note( + args.0.span, + "must implement `io::Write`, `fmt::Write`, or have a `write_fmt` method", + ); + if let ExprKind::Lit(_) = args.0.kind { + err.span_help( + args.0.span.shrink_to_lo(), + "a writer is needed before this format string", + ); + }; + + err + } + pub fn report_no_match_method_error( &self, mut span: Span, @@ -278,7 +300,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; // We could pass the file for long types into these two, but it isn't strictly necessary - // given how targetted they are. + // given how targeted they are. if self.suggest_wrapping_range_with_parens( tcx, rcvr_ty, @@ -323,16 +345,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - let mut err = struct_span_err!( - tcx.sess, - span, - E0599, - "no {} named `{}` found for {} `{}` in the current scope", - item_kind, - item_name, - rcvr_ty.prefix_string(self.tcx), - ty_str_reported, - ); + let is_write = sugg_span.ctxt().outer_expn_data().macro_def_id.map_or(false, |def_id| { + tcx.is_diagnostic_item(sym::write_macro, def_id) + || tcx.is_diagnostic_item(sym::writeln_macro, def_id) + }) && item_name.name == Symbol::intern("write_fmt"); + let mut err = if is_write + && let Some(args) = args + { + self.suggest_missing_writer(rcvr_ty, args) + } else { + struct_span_err!( + tcx.sess, + span, + E0599, + "no {} named `{}` found for {} `{}` in the current scope", + item_kind, + item_name, + rcvr_ty.prefix_string(self.tcx), + ty_str_reported, + ) + }; if tcx.sess.source_map().is_multiline(sugg_span) { err.span_label(sugg_span.with_hi(span.lo()), ""); } @@ -348,6 +380,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.downgrade_to_delayed_bug(); } + if tcx.ty_is_opaque_future(rcvr_ty) && item_name.name == sym::poll { + err.help(&format!( + "method `poll` found on `Pin<&mut {ty_str}>`, \ + see documentation for `std::pin::Pin`" + )); + err.help("self type must be pinned to call `Future::poll`, \ + see https://rust-lang.github.io/async-book/04_pinning/01_chapter.html#pinning-in-practice" + ); + } + if let Mode::MethodCall = mode && let SelfSource::MethodCall(cal) = source { self.suggest_await_before_method( &mut err, item_name, rcvr_ty, cal, span, expected.only_has_type(self), @@ -405,6 +447,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); probe.is_ok() }); + + self.note_internal_mutation_in_method( + &mut err, + rcvr_expr, + expected.to_option(&self), + rcvr_ty, + ); } let mut custom_span_label = false; @@ -612,19 +661,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Find all the requirements that come from a local `impl` block. let mut skip_list: FxHashSet<_> = Default::default(); let mut spanned_predicates = FxHashMap::default(); - for (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(data) - if matches!(p.kind().skip_binder(), ty::PredicateKind::Clause(_)) => - { - Some((p, parent, data.impl_or_alias_def_id, data)) + for (p, parent_p, cause) in unsatisfied_predicates { + // Extract the predicate span and parent def id of the cause, + // if we have one. + let (item_def_id, cause_span) = match cause.as_ref().map(|cause| cause.code()) { + Some(ObligationCauseCode::ImplDerivedObligation(data)) => { + (data.impl_or_alias_def_id, data.span) } - _ => None, - }) - { - match self.tcx.hir().get_if_local(impl_def_id) { + Some( + ObligationCauseCode::ExprBindingObligation(def_id, span, _, _) + | ObligationCauseCode::BindingObligation(def_id, span), + ) => (*def_id, *span), + _ => continue, + }; + + // Don't point out the span of `WellFormed` predicates. + if !matches!(p.kind().skip_binder(), ty::PredicateKind::Clause(_)) { + continue; + }; + + match self.tcx.hir().get_if_local(item_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 { @@ -669,7 +725,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }); for param in generics.params { - if param.span == cause.span && sized_pred { + 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"), @@ -692,9 +748,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (FxHashSet::default(), FxHashSet::default(), Vec::new()) }); entry.2.push(p); - if cause.span != *item_span { - entry.0.insert(cause.span); - entry.1.insert((cause.span, "unsatisfied trait bound introduced here")); + if cause_span != *item_span { + entry.0.insert(cause_span); + entry.1.insert((cause_span, "unsatisfied trait bound introduced here")); } else { if let Some(trait_ref) = of_trait { entry.0.insert(trait_ref.path.span); @@ -726,9 +782,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let entry = entry.or_insert_with(|| { (FxHashSet::default(), FxHashSet::default(), Vec::new()) }); - entry.0.insert(cause.span); + entry.0.insert(cause_span); entry.1.insert((ident.span, "")); - entry.1.insert((cause.span, "unsatisfied trait bound introduced here")); + entry.1.insert((cause_span, "unsatisfied trait bound introduced here")); entry.2.push(p); } Some(node) => unreachable!("encountered `{node:?}`"), @@ -1164,7 +1220,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .inputs() .skip_binder() .get(0) - .filter(|ty| ty.is_region_ptr() && !rcvr_ty.is_region_ptr()) + .filter(|ty| ty.is_ref() && !rcvr_ty.is_ref()) .copied() .unwrap_or(rcvr_ty), }; @@ -1247,7 +1303,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let target_ty = self .autoderef(sugg_span, rcvr_ty) .find(|(rcvr_ty, _)| { - DeepRejectCtxt { treat_obligation_params: TreatParams::AsInfer } + DeepRejectCtxt { treat_obligation_params: TreatParams::AsCandidateKey } .types_may_unify(*rcvr_ty, impl_ty) }) .map_or(impl_ty, |(ty, _)| ty) @@ -1506,7 +1562,7 @@ impl<'a, 'tcx> FnCtxt<'a, '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) + simplify_type(tcx, ty, TreatParams::AsCandidateKey) .and_then(|simp| { tcx.incoherent_impls(simp) .iter() @@ -1766,7 +1822,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .variants() .iter() .flat_map(|variant| { - let [field] = &variant.fields[..] else { return None; }; + let [field] = &variant.fields.raw[..] else { return None; }; let field_ty = field.ty(tcx, substs); // Skip `_`, since that'll just lead to ambiguity. @@ -2517,7 +2573,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if !candidates.is_empty() { // Sort from most relevant to least relevant. - candidates.sort_by(|a, b| a.cmp(b).reverse()); + candidates.sort_by_key(|&info| cmp::Reverse(info)); candidates.dedup(); let param_type = match rcvr_ty.kind() { @@ -2636,7 +2692,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // 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) + simplify_type(self.tcx, rcvr_ty, TreatParams::ForLookup) { let mut potential_candidates = Vec::new(); let mut explicitly_negative = Vec::new(); @@ -2651,7 +2707,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .any(|imp_did| { let imp = self.tcx.impl_trait_ref(imp_did).unwrap().subst_identity(); let imp_simp = - simplify_type(self.tcx, imp.self_ty(), TreatParams::AsPlaceholder); + simplify_type(self.tcx, imp.self_ty(), TreatParams::ForLookup); imp_simp.map_or(false, |s| s == simp_rcvr_ty) }) { |