From 29f31c58e9428ee6ad6c2faf2c391eea782f020d Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 6 Sep 2024 11:56:43 -0400 Subject: [PATCH 1/5] Don't call fn_arg_names for non-fn in resolver --- .../rustc_resolve/src/late/diagnostics.rs | 49 ++++++++++--------- .../auxiliary/foreign-trait-with-assoc.rs | 3 ++ .../dont-compute-arg-names-for-non-fn.rs | 11 +++++ .../dont-compute-arg-names-for-non-fn.stderr | 14 ++++++ 4 files changed, 53 insertions(+), 24 deletions(-) create mode 100644 tests/ui/resolve/auxiliary/foreign-trait-with-assoc.rs create mode 100644 tests/ui/resolve/dont-compute-arg-names-for-non-fn.rs create mode 100644 tests/ui/resolve/dont-compute-arg-names-for-non-fn.stderr diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 8f516c2db0900..2f5e1b1168d51 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -2068,33 +2068,34 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { ) { let res = binding.res(); if filter_fn(res) { - let def_id = res.def_id(); - let has_self = match def_id.as_local() { - Some(def_id) => { - self.r.delegation_fn_sigs.get(&def_id).map_or(false, |sig| sig.has_self) - } - None => self - .r - .tcx - .fn_arg_names(def_id) - .first() - .is_some_and(|ident| ident.name == kw::SelfLower), - }; - if has_self { - return Some(AssocSuggestion::MethodWithSelf { called }); - } else { - match res { - Res::Def(DefKind::AssocFn, _) => { + match res { + Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) => { + let has_self = match def_id.as_local() { + Some(def_id) => self + .r + .delegation_fn_sigs + .get(&def_id) + .map_or(false, |sig| sig.has_self), + None => self + .r + .tcx + .fn_arg_names(def_id) + .first() + .is_some_and(|ident| ident.name == kw::SelfLower), + }; + if has_self { + return Some(AssocSuggestion::MethodWithSelf { called }); + } else { return Some(AssocSuggestion::AssocFn { called }); } - Res::Def(DefKind::AssocConst, _) => { - return Some(AssocSuggestion::AssocConst); - } - Res::Def(DefKind::AssocTy, _) => { - return Some(AssocSuggestion::AssocType); - } - _ => {} } + Res::Def(DefKind::AssocConst, _) => { + return Some(AssocSuggestion::AssocConst); + } + Res::Def(DefKind::AssocTy, _) => { + return Some(AssocSuggestion::AssocType); + } + _ => {} } } } diff --git a/tests/ui/resolve/auxiliary/foreign-trait-with-assoc.rs b/tests/ui/resolve/auxiliary/foreign-trait-with-assoc.rs new file mode 100644 index 0000000000000..952957ec480fb --- /dev/null +++ b/tests/ui/resolve/auxiliary/foreign-trait-with-assoc.rs @@ -0,0 +1,3 @@ +pub trait Foo { + type Bar; +} diff --git a/tests/ui/resolve/dont-compute-arg-names-for-non-fn.rs b/tests/ui/resolve/dont-compute-arg-names-for-non-fn.rs new file mode 100644 index 0000000000000..20bbbff8fd20b --- /dev/null +++ b/tests/ui/resolve/dont-compute-arg-names-for-non-fn.rs @@ -0,0 +1,11 @@ +//@ aux-build:foreign-trait-with-assoc.rs + +extern crate foreign_trait_with_assoc; +use foreign_trait_with_assoc::Foo; + +// Make sure we don't try to call `fn_arg_names` on a non-fn item. + +impl Foo for Bar {} +//~^ ERROR cannot find type `Bar` in this scope + +fn main() {} diff --git a/tests/ui/resolve/dont-compute-arg-names-for-non-fn.stderr b/tests/ui/resolve/dont-compute-arg-names-for-non-fn.stderr new file mode 100644 index 0000000000000..a1a8bb575e14a --- /dev/null +++ b/tests/ui/resolve/dont-compute-arg-names-for-non-fn.stderr @@ -0,0 +1,14 @@ +error[E0412]: cannot find type `Bar` in this scope + --> $DIR/dont-compute-arg-names-for-non-fn.rs:8:14 + | +LL | impl Foo for Bar {} + | ^^^ + | +help: you might have meant to use the associated type + | +LL | impl Foo for Self::Bar {} + | ++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0412`. From d8a646fe77253232152f476386bb90fe562bd6f6 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 12 Sep 2024 14:32:55 -0400 Subject: [PATCH 2/5] Do not report an excessive number of overflow errors for an ever-growing deref impl --- compiler/rustc_hir_typeck/src/expr.rs | 13 +++++++- compiler/rustc_hir_typeck/src/method/probe.rs | 2 +- .../rustc_hir_typeck/src/method/suggest.rs | 2 +- .../methods/probe-error-on-infinite-deref.rs | 17 +++++++++++ .../probe-error-on-infinite-deref.stderr | 30 +++++++++++++++++++ 5 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 tests/ui/methods/probe-error-on-infinite-deref.rs create mode 100644 tests/ui/methods/probe-error-on-infinite-deref.stderr diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 6fd509ed32f6d..c03eeb1790e29 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -2942,7 +2942,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> 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() => { diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 3ba3429cbb331..5dc341653e5bd 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -375,7 +375,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // 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 9ede809ead29d..207cf4686b64b 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -672,7 +672,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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 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 0000000000000..c671092e6aa17 --- /dev/null +++ b/tests/ui/methods/probe-error-on-infinite-deref.rs @@ -0,0 +1,17 @@ +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 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 0000000000000..0d416a712220a --- /dev/null +++ b/tests/ui/methods/probe-error-on-infinite-deref.stderr @@ -0,0 +1,30 @@ +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[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`) + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +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 3 previous errors + +Some errors have detailed explanations: E0055, E0599. +For more information about an error, try `rustc --explain E0055`. From f95059b715f012e9f03043a52149b2857f589a3b Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 12 Sep 2024 14:33:37 -0400 Subject: [PATCH 3/5] Rename some methods to make it clear they're only for diagnostics --- compiler/rustc_hir_typeck/src/expr.rs | 34 +++++++++++-------- .../rustc_hir_typeck/src/method/suggest.rs | 11 +++--- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index c03eeb1790e29..821a90d7a8c44 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -2864,13 +2864,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (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 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .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>, @@ -2986,7 +2986,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// 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, @@ -3011,20 +3011,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 207cf4686b64b..7df36864fe356 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -2675,9 +2675,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) { 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 +2696,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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( From 575c15a72ed339073bd446fa216c7dc31e2d5d2a Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 12 Sep 2024 14:47:22 -0400 Subject: [PATCH 4/5] Use Autoderef::silence_errors more liberally throughout diagnostics code --- compiler/rustc_hir_typeck/src/coercion.rs | 2 +- .../rustc_hir_typeck/src/method/suggest.rs | 36 ++++++++++--------- compiler/rustc_hir_typeck/src/pat.rs | 1 + .../methods/probe-error-on-infinite-deref.rs | 1 - .../probe-error-on-infinite-deref.stderr | 11 +----- 5 files changed, 23 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 3bada1de148b1..fca7babea30d5 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -1049,7 +1049,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// 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/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 7df36864fe356..deabf693af2f5 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -62,14 +62,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // 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 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } 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( @@ -2237,6 +2239,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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); @@ -3198,7 +3202,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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, @@ -4224,7 +4228,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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 25f9340eeb717..c07100a81e67b 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -2533,6 +2533,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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 index c671092e6aa17..85c1c0c09c13e 100644 --- a/tests/ui/methods/probe-error-on-infinite-deref.rs +++ b/tests/ui/methods/probe-error-on-infinite-deref.rs @@ -12,6 +12,5 @@ impl Deref for Wrap { fn main() { Wrap(1).lmao(); //~^ ERROR reached the recursion limit - //~| 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 index 0d416a712220a..57a9ca2eaa80f 100644 --- a/tests/ui/methods/probe-error-on-infinite-deref.stderr +++ b/tests/ui/methods/probe-error-on-infinite-deref.stderr @@ -6,15 +6,6 @@ LL | Wrap(1).lmao(); | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`probe_error_on_infinite_deref`) -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`) - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error[E0599]: no method named `lmao` found for struct `Wrap<{integer}>` in the current scope --> $DIR/probe-error-on-infinite-deref.rs:13:13 | @@ -24,7 +15,7 @@ LL | struct Wrap(T); LL | Wrap(1).lmao(); | ^^^^ method not found in `Wrap<{integer}>` -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors Some errors have detailed explanations: E0055, E0599. For more information about an error, try `rustc --explain E0055`. From 45eceb2c576d20dd43c22ae4b591a4f1e9d1db56 Mon Sep 17 00:00:00 2001 From: Jesse Rusak Date: Mon, 16 Sep 2024 11:26:26 -0400 Subject: [PATCH 5/5] Avoid crashing on variadic functions when producing arg-mismatch errors --- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 17 +++++++++++++---- tests/crashes/130372-1.rs | 9 --------- tests/crashes/130372-2.rs | 11 ----------- tests/crashes/130372-3.rs | 7 ------- .../mismatch-args-vargs-issue-130372.rs | 12 ++++++++++++ .../mismatch-args-vargs-issue-130372.stderr | 19 +++++++++++++++++++ 6 files changed, 44 insertions(+), 31 deletions(-) delete mode 100644 tests/crashes/130372-1.rs delete mode 100644 tests/crashes/130372-2.rs delete mode 100644 tests/crashes/130372-3.rs create mode 100644 tests/ui/mismatched_types/mismatch-args-vargs-issue-130372.rs create mode 100644 tests/ui/mismatched_types/mismatch-args-vargs-issue-130372.stderr diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 8348c6e9a169e..8810f14aaa95c 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -2619,9 +2619,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { is_method: bool, ) -> Option>, &hir::Param<'_>)>> { let fn_node = self.tcx.hir().get_if_local(def_id)?; + let fn_decl = fn_node.fn_decl()?; - let generic_params: Vec>> = fn_node - .fn_decl()? + let generic_params: Vec>> = fn_decl .inputs .into_iter() .skip(if is_method { 1 } else { 0 }) @@ -2642,7 +2642,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }) .collect(); - let params: Vec<&hir::Param<'_>> = self + let mut params: Vec<&hir::Param<'_>> = self .tcx .hir() .body(fn_node.body_id()?) @@ -2651,7 +2651,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .skip(if is_method { 1 } else { 0 }) .collect(); - Some(generic_params.into_iter().zip_eq(params).collect()) + // The surrounding code expects variadic functions to not have a parameter representing + // the "..." parameter. This is already true of the FnDecl but not of the body params, so + // we drop it if it exists. + + if fn_decl.c_variadic { + params.pop(); + } + + debug_assert_eq!(params.len(), generic_params.len()); + Some(generic_params.into_iter().zip(params).collect()) } } diff --git a/tests/crashes/130372-1.rs b/tests/crashes/130372-1.rs deleted file mode 100644 index 5d58c14e7abf1..0000000000000 --- a/tests/crashes/130372-1.rs +++ /dev/null @@ -1,9 +0,0 @@ -//@ known-bug: rust-lang/rust#130372 - -pub fn variadic_fn(n: usize, mut args: ...) {} - -reuse variadic_fn; - -fn main() { - variadic_fn(); -} diff --git a/tests/crashes/130372-2.rs b/tests/crashes/130372-2.rs deleted file mode 100644 index 46404191e329c..0000000000000 --- a/tests/crashes/130372-2.rs +++ /dev/null @@ -1,11 +0,0 @@ -//@ known-bug: rust-lang/rust#130372 - -pub fn test_va_copy(_: u64, mut ap: ...) {} - -pub fn main() { - unsafe { - test_va_copy(); - - call(x); - } -} diff --git a/tests/crashes/130372-3.rs b/tests/crashes/130372-3.rs deleted file mode 100644 index 6e1c57437c8a1..0000000000000 --- a/tests/crashes/130372-3.rs +++ /dev/null @@ -1,7 +0,0 @@ -//@ known-bug: rust-lang/rust#130372 - -fn bar() -> impl Fn() { - wrap() -} - -fn wrap(...: impl ...) -> impl Fn() {} diff --git a/tests/ui/mismatched_types/mismatch-args-vargs-issue-130372.rs b/tests/ui/mismatched_types/mismatch-args-vargs-issue-130372.rs new file mode 100644 index 0000000000000..60a3b47010e29 --- /dev/null +++ b/tests/ui/mismatched_types/mismatch-args-vargs-issue-130372.rs @@ -0,0 +1,12 @@ +#![feature(c_variadic)] + +// Regression test that covers all 3 cases of https://github.com/rust-lang/rust/issues/130372 + +unsafe extern "C" fn test_va_copy(_: u64, mut ap: ...) {} + +pub fn main() { + unsafe { + test_va_copy(); + //~^ ERROR this function takes at least 1 argument but 0 arguments were supplied + } +} diff --git a/tests/ui/mismatched_types/mismatch-args-vargs-issue-130372.stderr b/tests/ui/mismatched_types/mismatch-args-vargs-issue-130372.stderr new file mode 100644 index 0000000000000..38f769703582c --- /dev/null +++ b/tests/ui/mismatched_types/mismatch-args-vargs-issue-130372.stderr @@ -0,0 +1,19 @@ +error[E0060]: this function takes at least 1 argument but 0 arguments were supplied + --> $DIR/mismatch-args-vargs-issue-130372.rs:9:9 + | +LL | test_va_copy(); + | ^^^^^^^^^^^^-- argument #1 of type `u64` is missing + | +note: function defined here + --> $DIR/mismatch-args-vargs-issue-130372.rs:5:22 + | +LL | unsafe extern "C" fn test_va_copy(_: u64, mut ap: ...) {} + | ^^^^^^^^^^^^ ------ +help: provide the argument + | +LL | test_va_copy(/* u64 */); + | ~~~~~~~~~~~ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0060`.