diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 3bada1de148..fca7babea30 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -1049,7 +1049,7 @@ pub(crate) fn deref_steps(&self, expr_ty: Ty<'tcx>, target: Ty<'tcx>) -> Option< /// trait or region sub-obligations. (presumably we could, but it's not /// particularly important for diagnostics...) pub(crate) fn deref_once_mutably_for_diagnostic(&self, expr_ty: Ty<'tcx>) -> Option> { - self.autoderef(DUMMY_SP, expr_ty).nth(1).and_then(|(deref_ty, _)| { + self.autoderef(DUMMY_SP, expr_ty).silence_errors().nth(1).and_then(|(deref_ty, _)| { self.infcx .type_implements_trait( self.tcx.lang_items().deref_mut_trait()?, diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 6fd509ed32f..821a90d7a8c 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -2864,13 +2864,13 @@ fn no_such_field_err(&self, field: Ident, expr_t: Ty<'tcx>, id: HirId) -> Diag<' (expr_t, "") }; for (found_fields, args) in - self.get_field_candidates_considering_privacy(span, ty, mod_id, id) + self.get_field_candidates_considering_privacy_for_diag(span, ty, mod_id, id) { let field_names = found_fields.iter().map(|field| field.name).collect::>(); let mut candidate_fields: Vec<_> = found_fields .into_iter() .filter_map(|candidate_field| { - self.check_for_nested_field_satisfying( + self.check_for_nested_field_satisfying_condition_for_diag( span, &|candidate_field, _| candidate_field.ident(self.tcx()) == field, candidate_field, @@ -2933,7 +2933,7 @@ fn private_field_err(&self, field: Ident, base_did: DefId) -> Diag<'_> { .with_span_label(field.span, "private field") } - pub(crate) fn get_field_candidates_considering_privacy( + pub(crate) fn get_field_candidates_considering_privacy_for_diag( &self, span: Span, base_ty: Ty<'tcx>, @@ -2942,7 +2942,18 @@ pub(crate) fn get_field_candidates_considering_privacy( ) -> Vec<(Vec<&'tcx ty::FieldDef>, GenericArgsRef<'tcx>)> { debug!("get_field_candidates(span: {:?}, base_t: {:?}", span, base_ty); - self.autoderef(span, base_ty) + let mut autoderef = self.autoderef(span, base_ty).silence_errors(); + let deref_chain: Vec<_> = autoderef.by_ref().collect(); + + // Don't probe if we hit the recursion limit, since it may result in + // quadratic blowup if we then try to further deref the results of this + // function. This is a best-effort method, after all. + if autoderef.reached_recursion_limit() { + return vec![]; + } + + deref_chain + .into_iter() .filter_map(move |(base_t, _)| { match base_t.kind() { ty::Adt(base_def, args) if !base_def.is_enum() => { @@ -2975,7 +2986,7 @@ pub(crate) fn get_field_candidates_considering_privacy( /// This method is called after we have encountered a missing field error to recursively /// search for the field - pub(crate) fn check_for_nested_field_satisfying( + pub(crate) fn check_for_nested_field_satisfying_condition_for_diag( &self, span: Span, matches: &impl Fn(&ty::FieldDef, Ty<'tcx>) -> bool, @@ -3000,20 +3011,24 @@ pub(crate) fn check_for_nested_field_satisfying( if matches(candidate_field, field_ty) { return Some(field_path); } else { - for (nested_fields, subst) in - self.get_field_candidates_considering_privacy(span, field_ty, mod_id, hir_id) + for (nested_fields, subst) in self + .get_field_candidates_considering_privacy_for_diag( + span, field_ty, mod_id, hir_id, + ) { // recursively search fields of `candidate_field` if it's a ty::Adt for field in nested_fields { - if let Some(field_path) = self.check_for_nested_field_satisfying( - span, - matches, - field, - subst, - field_path.clone(), - mod_id, - hir_id, - ) { + if let Some(field_path) = self + .check_for_nested_field_satisfying_condition_for_diag( + span, + matches, + field, + subst, + field_path.clone(), + mod_id, + hir_id, + ) + { return Some(field_path); } } diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 3ba3429cbb3..5dc341653e5 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -375,7 +375,7 @@ pub(crate) fn probe_op( // 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 { + if steps.reached_recursion_limit && !is_suggestion.0 { self.probe(|_| { let ty = &steps .steps diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 9ede809ead2..deabf693af2 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -62,14 +62,14 @@ fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool { // 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, _)| { + if self.autoderef(span, ty).silence_errors().any(|(ty, _)| { info!("check deref {:?} error", ty); matches!(ty.kind(), ty::Error(_) | ty::Infer(_)) }) { return false; } - self.autoderef(span, ty).any(|(ty, _)| { + self.autoderef(span, ty).silence_errors().any(|(ty, _)| { info!("check deref {:?} impl FnOnce", ty); self.probe(|_| { let trait_ref = @@ -90,7 +90,9 @@ fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool { } fn is_slice_ty(&self, ty: Ty<'tcx>, span: Span) -> bool { - self.autoderef(span, ty).any(|(ty, _)| matches!(ty.kind(), ty::Slice(..) | ty::Array(..))) + self.autoderef(span, ty) + .silence_errors() + .any(|(ty, _)| matches!(ty.kind(), ty::Slice(..) | ty::Array(..))) } fn impl_into_iterator_should_be_iterator( @@ -672,7 +674,7 @@ fn report_no_match_method_error( let mut ty_str_reported = ty_str.clone(); if let ty::Adt(_, generics) = rcvr_ty.kind() { if generics.len() > 0 { - let mut autoderef = self.autoderef(span, rcvr_ty); + let mut autoderef = self.autoderef(span, rcvr_ty).silence_errors(); let candidate_found = autoderef.any(|(ty, _)| { if let ty::Adt(adt_def, _) = ty.kind() { self.tcx @@ -2237,6 +2239,7 @@ fn suggest_associated_call_syntax( let impl_ty = self.tcx.type_of(*impl_did).instantiate_identity(); let target_ty = self .autoderef(sugg_span, rcvr_ty) + .silence_errors() .find(|(rcvr_ty, _)| { DeepRejectCtxt::relate_rigid_infer(self.tcx).types_may_unify(*rcvr_ty, impl_ty) }) @@ -2352,17 +2355,18 @@ fn suggest_calling_field_as_fn( err: &mut Diag<'_>, ) -> bool { let tcx = self.tcx; - let field_receiver = self.autoderef(span, rcvr_ty).find_map(|(ty, _)| match ty.kind() { - ty::Adt(def, args) 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, args); - (field, field_ty) - }) - } - _ => None, - }); + let field_receiver = + self.autoderef(span, rcvr_ty).silence_errors().find_map(|(ty, _)| match ty.kind() { + ty::Adt(def, args) 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, args); + (field, field_ty) + }) + } + _ => None, + }); if let Some((field, field_ty)) = field_receiver { let scope = tcx.parent_module_from_def_id(self.body_id); let is_accessible = field.vis.is_accessible_from(scope, tcx); @@ -2675,9 +2679,12 @@ fn suggest_calling_method_on_field( ) { if let SelfSource::MethodCall(expr) = source { let mod_id = self.tcx.parent_module(expr.hir_id).to_def_id(); - for (fields, args) in - self.get_field_candidates_considering_privacy(span, actual, mod_id, expr.hir_id) - { + for (fields, args) in self.get_field_candidates_considering_privacy_for_diag( + span, + actual, + mod_id, + expr.hir_id, + ) { let call_expr = self.tcx.hir().expect_expr(self.tcx.parent_hir_id(expr.hir_id)); let lang_items = self.tcx.lang_items(); @@ -2693,7 +2700,7 @@ fn suggest_calling_method_on_field( let mut candidate_fields: Vec<_> = fields .into_iter() .filter_map(|candidate_field| { - self.check_for_nested_field_satisfying( + self.check_for_nested_field_satisfying_condition_for_diag( span, &|_, field_ty| { self.lookup_probe_for_diagnostic( @@ -3195,7 +3202,7 @@ fn note_derefed_ty_has_method( let SelfSource::QPath(ty) = self_source else { return; }; - for (deref_ty, _) in self.autoderef(DUMMY_SP, rcvr_ty).skip(1) { + for (deref_ty, _) in self.autoderef(DUMMY_SP, rcvr_ty).silence_errors().skip(1) { if let Ok(pick) = self.probe_for_name( Mode::Path, item_name, @@ -4221,7 +4228,7 @@ fn is_local(ty: Ty<'_>) -> bool { return is_local(rcvr_ty); } - self.autoderef(span, rcvr_ty).any(|(ty, _)| is_local(ty)) + self.autoderef(span, rcvr_ty).silence_errors().any(|(ty, _)| is_local(ty)) } } diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index c8c95ddcfce..45a6efc7a6a 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -2533,6 +2533,7 @@ fn error_expected_array_or_slice( err.help("the semantics of slice patterns changed recently; see issue #62254"); } else if self .autoderef(span, expected_ty) + .silence_errors() .any(|(ty, _)| matches!(ty.kind(), ty::Slice(..) | ty::Array(..))) && let Some(span) = ti.span && let Some(_) = ti.origin_expr diff --git a/tests/ui/methods/probe-error-on-infinite-deref.rs b/tests/ui/methods/probe-error-on-infinite-deref.rs new file mode 100644 index 00000000000..85c1c0c09c1 --- /dev/null +++ b/tests/ui/methods/probe-error-on-infinite-deref.rs @@ -0,0 +1,16 @@ +use std::ops::Deref; + +// Make sure that method probe error reporting doesn't get too tangled up +// on this infinite deref impl. See #130224. + +struct Wrap(T); +impl Deref for Wrap { + type Target = Wrap>; + fn deref(&self) -> &Wrap> { todo!() } +} + +fn main() { + Wrap(1).lmao(); + //~^ ERROR reached the recursion limit + //~| ERROR no method named `lmao` +} diff --git a/tests/ui/methods/probe-error-on-infinite-deref.stderr b/tests/ui/methods/probe-error-on-infinite-deref.stderr new file mode 100644 index 00000000000..57a9ca2eaa8 --- /dev/null +++ b/tests/ui/methods/probe-error-on-infinite-deref.stderr @@ -0,0 +1,21 @@ +error[E0055]: reached the recursion limit while auto-dereferencing `Wrap>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` + --> $DIR/probe-error-on-infinite-deref.rs:13:13 + | +LL | Wrap(1).lmao(); + | ^^^^ deref recursion limit reached + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`probe_error_on_infinite_deref`) + +error[E0599]: no method named `lmao` found for struct `Wrap<{integer}>` in the current scope + --> $DIR/probe-error-on-infinite-deref.rs:13:13 + | +LL | struct Wrap(T); + | -------------- method `lmao` not found for this struct +... +LL | Wrap(1).lmao(); + | ^^^^ method not found in `Wrap<{integer}>` + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0055, E0599. +For more information about an error, try `rustc --explain E0055`.