Rollup merge of #130282 - compiler-errors:over-overflow, r=BoxyUwU

Do not report an excessive number of overflow errors for an ever-growing deref impl

Check that we don't first hit the recursion limit in `get_field_candidates_considering_privacy` before probing for methods when we have a method lookup failure and we want to see if `.field.method()` exists. We also silence overflow error messages if we're probing for methods for diagnostics.

Also renames some functions to make it clearer that they're only for diagnostics, and sprinkle some `Autoderef::silence_errors` around to silence unnecessary overflow errors that come from diagnostics.

Fixes #130224.
This commit is contained in:
Matthias Krüger 2024-09-16 21:53:07 +02:00 committed by GitHub
commit 852e08e9bc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 99 additions and 39 deletions

View File

@ -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 /// trait or region sub-obligations. (presumably we could, but it's not
/// particularly important for diagnostics...) /// particularly important for diagnostics...)
pub(crate) fn deref_once_mutably_for_diagnostic(&self, expr_ty: Ty<'tcx>) -> Option<Ty<'tcx>> { pub(crate) fn deref_once_mutably_for_diagnostic(&self, expr_ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
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 self.infcx
.type_implements_trait( .type_implements_trait(
self.tcx.lang_items().deref_mut_trait()?, self.tcx.lang_items().deref_mut_trait()?,

View File

@ -2864,13 +2864,13 @@ fn no_such_field_err(&self, field: Ident, expr_t: Ty<'tcx>, id: HirId) -> Diag<'
(expr_t, "") (expr_t, "")
}; };
for (found_fields, args) in 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::<Vec<_>>(); let field_names = found_fields.iter().map(|field| field.name).collect::<Vec<_>>();
let mut candidate_fields: Vec<_> = found_fields let mut candidate_fields: Vec<_> = found_fields
.into_iter() .into_iter()
.filter_map(|candidate_field| { .filter_map(|candidate_field| {
self.check_for_nested_field_satisfying( self.check_for_nested_field_satisfying_condition_for_diag(
span, span,
&|candidate_field, _| candidate_field.ident(self.tcx()) == field, &|candidate_field, _| candidate_field.ident(self.tcx()) == field,
candidate_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") .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, &self,
span: Span, span: Span,
base_ty: Ty<'tcx>, base_ty: Ty<'tcx>,
@ -2942,7 +2942,18 @@ pub(crate) fn get_field_candidates_considering_privacy(
) -> Vec<(Vec<&'tcx ty::FieldDef>, GenericArgsRef<'tcx>)> { ) -> Vec<(Vec<&'tcx ty::FieldDef>, GenericArgsRef<'tcx>)> {
debug!("get_field_candidates(span: {:?}, base_t: {:?}", span, base_ty); 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, _)| { .filter_map(move |(base_t, _)| {
match base_t.kind() { match base_t.kind() {
ty::Adt(base_def, args) if !base_def.is_enum() => { 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 /// This method is called after we have encountered a missing field error to recursively
/// search for the field /// search for the field
pub(crate) fn check_for_nested_field_satisfying( pub(crate) fn check_for_nested_field_satisfying_condition_for_diag(
&self, &self,
span: Span, span: Span,
matches: &impl Fn(&ty::FieldDef, Ty<'tcx>) -> bool, 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) { if matches(candidate_field, field_ty) {
return Some(field_path); return Some(field_path);
} else { } else {
for (nested_fields, subst) in for (nested_fields, subst) in self
self.get_field_candidates_considering_privacy(span, field_ty, mod_id, hir_id) .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 // recursively search fields of `candidate_field` if it's a ty::Adt
for field in nested_fields { for field in nested_fields {
if let Some(field_path) = self.check_for_nested_field_satisfying( if let Some(field_path) = self
span, .check_for_nested_field_satisfying_condition_for_diag(
matches, span,
field, matches,
subst, field,
field_path.clone(), subst,
mod_id, field_path.clone(),
hir_id, mod_id,
) { hir_id,
)
{
return Some(field_path); return Some(field_path);
} }
} }

View File

@ -375,7 +375,7 @@ pub(crate) fn probe_op<OP, R>(
// If our autoderef loop had reached the recursion limit, // If our autoderef loop had reached the recursion limit,
// report an overflow error, but continue going on with // report an overflow error, but continue going on with
// the truncated autoderef list. // the truncated autoderef list.
if steps.reached_recursion_limit { if steps.reached_recursion_limit && !is_suggestion.0 {
self.probe(|_| { self.probe(|_| {
let ty = &steps let ty = &steps
.steps .steps

View File

@ -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`, // 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, // but since a Dummy binder is used to fill in the FnOnce trait's arguments,
// type resolution always gives a "maybe" here. // 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); info!("check deref {:?} error", ty);
matches!(ty.kind(), ty::Error(_) | ty::Infer(_)) matches!(ty.kind(), ty::Error(_) | ty::Infer(_))
}) { }) {
return false; return false;
} }
self.autoderef(span, ty).any(|(ty, _)| { self.autoderef(span, ty).silence_errors().any(|(ty, _)| {
info!("check deref {:?} impl FnOnce", ty); info!("check deref {:?} impl FnOnce", ty);
self.probe(|_| { self.probe(|_| {
let trait_ref = 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 { 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( 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(); let mut ty_str_reported = ty_str.clone();
if let ty::Adt(_, generics) = rcvr_ty.kind() { if let ty::Adt(_, generics) = rcvr_ty.kind() {
if generics.len() > 0 { 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, _)| { let candidate_found = autoderef.any(|(ty, _)| {
if let ty::Adt(adt_def, _) = ty.kind() { if let ty::Adt(adt_def, _) = ty.kind() {
self.tcx self.tcx
@ -2237,6 +2239,7 @@ fn suggest_associated_call_syntax(
let impl_ty = self.tcx.type_of(*impl_did).instantiate_identity(); let impl_ty = self.tcx.type_of(*impl_did).instantiate_identity();
let target_ty = self let target_ty = self
.autoderef(sugg_span, rcvr_ty) .autoderef(sugg_span, rcvr_ty)
.silence_errors()
.find(|(rcvr_ty, _)| { .find(|(rcvr_ty, _)| {
DeepRejectCtxt::relate_rigid_infer(self.tcx).types_may_unify(*rcvr_ty, impl_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<'_>, err: &mut Diag<'_>,
) -> bool { ) -> bool {
let tcx = self.tcx; let tcx = self.tcx;
let field_receiver = self.autoderef(span, rcvr_ty).find_map(|(ty, _)| match ty.kind() { let field_receiver =
ty::Adt(def, args) if !def.is_enum() => { self.autoderef(span, rcvr_ty).silence_errors().find_map(|(ty, _)| match ty.kind() {
let variant = &def.non_enum_variant(); ty::Adt(def, args) if !def.is_enum() => {
tcx.find_field_index(item_name, variant).map(|index| { let variant = &def.non_enum_variant();
let field = &variant.fields[index]; tcx.find_field_index(item_name, variant).map(|index| {
let field_ty = field.ty(tcx, args); let field = &variant.fields[index];
(field, field_ty) let field_ty = field.ty(tcx, args);
}) (field, field_ty)
} })
_ => None, }
}); _ => None,
});
if let Some((field, field_ty)) = field_receiver { if let Some((field, field_ty)) = field_receiver {
let scope = tcx.parent_module_from_def_id(self.body_id); let scope = tcx.parent_module_from_def_id(self.body_id);
let is_accessible = field.vis.is_accessible_from(scope, tcx); 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 { if let SelfSource::MethodCall(expr) = source {
let mod_id = self.tcx.parent_module(expr.hir_id).to_def_id(); let mod_id = self.tcx.parent_module(expr.hir_id).to_def_id();
for (fields, args) in for (fields, args) in self.get_field_candidates_considering_privacy_for_diag(
self.get_field_candidates_considering_privacy(span, actual, mod_id, expr.hir_id) span,
{ actual,
mod_id,
expr.hir_id,
) {
let call_expr = self.tcx.hir().expect_expr(self.tcx.parent_hir_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(); let lang_items = self.tcx.lang_items();
@ -2693,7 +2700,7 @@ fn suggest_calling_method_on_field(
let mut candidate_fields: Vec<_> = fields let mut candidate_fields: Vec<_> = fields
.into_iter() .into_iter()
.filter_map(|candidate_field| { .filter_map(|candidate_field| {
self.check_for_nested_field_satisfying( self.check_for_nested_field_satisfying_condition_for_diag(
span, span,
&|_, field_ty| { &|_, field_ty| {
self.lookup_probe_for_diagnostic( self.lookup_probe_for_diagnostic(
@ -3195,7 +3202,7 @@ fn note_derefed_ty_has_method(
let SelfSource::QPath(ty) = self_source else { let SelfSource::QPath(ty) = self_source else {
return; 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( if let Ok(pick) = self.probe_for_name(
Mode::Path, Mode::Path,
item_name, item_name,
@ -4221,7 +4228,7 @@ fn is_local(ty: Ty<'_>) -> bool {
return is_local(rcvr_ty); 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))
} }
} }

View File

@ -2533,6 +2533,7 @@ fn error_expected_array_or_slice(
err.help("the semantics of slice patterns changed recently; see issue #62254"); err.help("the semantics of slice patterns changed recently; see issue #62254");
} else if self } else if self
.autoderef(span, expected_ty) .autoderef(span, expected_ty)
.silence_errors()
.any(|(ty, _)| matches!(ty.kind(), ty::Slice(..) | ty::Array(..))) .any(|(ty, _)| matches!(ty.kind(), ty::Slice(..) | ty::Array(..)))
&& let Some(span) = ti.span && let Some(span) = ti.span
&& let Some(_) = ti.origin_expr && let Some(_) = ti.origin_expr

View File

@ -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>(T);
impl<T> Deref for Wrap<T> {
type Target = Wrap<Wrap<T>>;
fn deref(&self) -> &Wrap<Wrap<T>> { todo!() }
}
fn main() {
Wrap(1).lmao();
//~^ ERROR reached the recursion limit
//~| ERROR no method named `lmao`
}

View File

@ -0,0 +1,21 @@
error[E0055]: reached the recursion limit while auto-dereferencing `Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<Wrap<{integer}>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>`
--> $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>(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`.