Skip to content

Commit 4860ac5

Browse files
committed
Only collect infer vars to error about in case infer vars are actually forbidden
1 parent b5282d6 commit 4860ac5

File tree

3 files changed

+118
-85
lines changed

3 files changed

+118
-85
lines changed

compiler/rustc_hir_analysis/src/collect.rs

Lines changed: 91 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@ use rustc_data_structures::captures::Captures;
1919
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
2020
use rustc_data_structures::unord::UnordMap;
2121
use rustc_errors::{struct_span_code_err, Applicability, Diag, ErrorGuaranteed, StashKey, E0228};
22-
use rustc_hir as hir;
2322
use rustc_hir::def::DefKind;
2423
use rustc_hir::def_id::{DefId, LocalDefId};
25-
use rustc_hir::intravisit::{self, Visitor};
24+
use rustc_hir::intravisit::{self, walk_generics, Visitor};
25+
use rustc_hir::{self as hir};
2626
use rustc_hir::{GenericParamKind, Node};
2727
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
2828
use rustc_infer::traits::ObligationCause;
@@ -526,6 +526,95 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> {
526526
fn infcx(&self) -> &InferCtxt<'tcx> {
527527
&self.infcx
528528
}
529+
530+
fn lower_fn_sig(
531+
&self,
532+
decl: &hir::FnDecl<'tcx>,
533+
generics: Option<&hir::Generics<'_>>,
534+
hir_id: rustc_hir::HirId,
535+
hir_ty: Option<&hir::Ty<'_>>,
536+
) -> (Vec<Ty<'tcx>>, Ty<'tcx>) {
537+
let tcx = self.tcx();
538+
// We proactively collect all the inferred type params to emit a single error per fn def.
539+
let mut visitor = HirPlaceholderCollector::default();
540+
let mut infer_replacements = vec![];
541+
542+
if let Some(generics) = generics {
543+
walk_generics(&mut visitor, generics);
544+
}
545+
546+
let input_tys = decl
547+
.inputs
548+
.iter()
549+
.enumerate()
550+
.map(|(i, a)| {
551+
if let hir::TyKind::Infer = a.kind {
552+
if let Some(suggested_ty) =
553+
self.lowerer().suggest_trait_fn_ty_for_impl_fn_infer(hir_id, Some(i))
554+
{
555+
infer_replacements.push((a.span, suggested_ty.to_string()));
556+
return Ty::new_error_with_message(
557+
self.tcx(),
558+
a.span,
559+
suggested_ty.to_string(),
560+
);
561+
}
562+
}
563+
564+
// Only visit the type looking for `_` if we didn't fix the type above
565+
visitor.visit_ty(a);
566+
self.lowerer().lower_arg_ty(a, None)
567+
})
568+
.collect();
569+
570+
let output_ty = match decl.output {
571+
hir::FnRetTy::Return(output) => {
572+
if let hir::TyKind::Infer = output.kind
573+
&& let Some(suggested_ty) =
574+
self.lowerer().suggest_trait_fn_ty_for_impl_fn_infer(hir_id, None)
575+
{
576+
infer_replacements.push((output.span, suggested_ty.to_string()));
577+
Ty::new_error_with_message(self.tcx(), output.span, suggested_ty.to_string())
578+
} else {
579+
visitor.visit_ty(output);
580+
self.lower_ty(output)
581+
}
582+
}
583+
hir::FnRetTy::DefaultReturn(..) => tcx.types.unit,
584+
};
585+
586+
if !(visitor.0.is_empty() && infer_replacements.is_empty()) {
587+
// We always collect the spans for placeholder types when evaluating `fn`s, but we
588+
// only want to emit an error complaining about them if infer types (`_`) are not
589+
// allowed. `allow_infer` gates this behavior. We check for the presence of
590+
// `ident_span` to not emit an error twice when we have `fn foo(_: fn() -> _)`.
591+
592+
let mut diag = crate::collect::placeholder_type_error_diag(
593+
tcx,
594+
generics,
595+
visitor.0,
596+
infer_replacements.iter().map(|(s, _)| *s).collect(),
597+
true,
598+
hir_ty,
599+
"function",
600+
);
601+
602+
if !infer_replacements.is_empty() {
603+
diag.multipart_suggestion(
604+
format!(
605+
"try replacing `_` with the type{} in the corresponding trait method signature",
606+
rustc_errors::pluralize!(infer_replacements.len()),
607+
),
608+
infer_replacements,
609+
Applicability::MachineApplicable,
610+
);
611+
}
612+
613+
self.set_tainted_by_errors(diag.emit());
614+
}
615+
616+
(input_tys, output_ty)
617+
}
529618
}
530619

531620
/// Synthesize a new lifetime name that doesn't clash with any of the lifetimes already present.

compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs

Lines changed: 10 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ mod lint;
2020
mod object_safety;
2121

2222
use crate::bounds::Bounds;
23-
use crate::collect::HirPlaceholderCollector;
2423
use crate::errors::{AmbiguousLifetimeBound, WildPatTy};
2524
use crate::hir_ty_lowering::errors::{prohibit_assoc_item_binding, GenericsArgsErrExtend};
2625
use crate::hir_ty_lowering::generics::{check_generic_arg_count, lower_generic_args};
@@ -34,7 +33,6 @@ use rustc_errors::{
3433
use rustc_hir as hir;
3534
use rustc_hir::def::{CtorOf, DefKind, Namespace, Res};
3635
use rustc_hir::def_id::{DefId, LocalDefId};
37-
use rustc_hir::intravisit::{walk_generics, Visitor as _};
3836
use rustc_hir::{GenericArg, GenericArgs, HirId};
3937
use rustc_infer::infer::InferCtxt;
4038
use rustc_infer::traits::ObligationCause;
@@ -157,6 +155,14 @@ pub trait HirTyLowerer<'tcx> {
157155
poly_trait_ref: ty::PolyTraitRef<'tcx>,
158156
) -> Ty<'tcx>;
159157

158+
fn lower_fn_sig(
159+
&self,
160+
decl: &hir::FnDecl<'tcx>,
161+
generics: Option<&hir::Generics<'_>>,
162+
hir_id: HirId,
163+
hir_ty: Option<&hir::Ty<'_>>,
164+
) -> (Vec<Ty<'tcx>>, Ty<'tcx>);
165+
160166
/// Returns `AdtDef` if `ty` is an ADT.
161167
///
162168
/// Note that `ty` might be a alias type that needs normalization.
@@ -2270,92 +2276,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
22702276
let bound_vars = tcx.late_bound_vars(hir_id);
22712277
debug!(?bound_vars);
22722278

2273-
// We proactively collect all the inferred type params to emit a single error per fn def.
2274-
let mut visitor = HirPlaceholderCollector::default();
2275-
let mut infer_replacements = vec![];
2276-
2277-
if let Some(generics) = generics {
2278-
walk_generics(&mut visitor, generics);
2279-
}
2280-
2281-
let input_tys: Vec<_> = decl
2282-
.inputs
2283-
.iter()
2284-
.enumerate()
2285-
.map(|(i, a)| {
2286-
if let hir::TyKind::Infer = a.kind
2287-
&& !self.allow_infer()
2288-
{
2289-
if let Some(suggested_ty) =
2290-
self.suggest_trait_fn_ty_for_impl_fn_infer(hir_id, Some(i))
2291-
{
2292-
infer_replacements.push((a.span, suggested_ty.to_string()));
2293-
return Ty::new_error_with_message(
2294-
self.tcx(),
2295-
a.span,
2296-
suggested_ty.to_string(),
2297-
);
2298-
}
2299-
}
2300-
2301-
// Only visit the type looking for `_` if we didn't fix the type above
2302-
visitor.visit_ty(a);
2303-
self.lower_arg_ty(a, None)
2304-
})
2305-
.collect();
2306-
2307-
let output_ty = match decl.output {
2308-
hir::FnRetTy::Return(output) => {
2309-
if let hir::TyKind::Infer = output.kind
2310-
&& !self.allow_infer()
2311-
&& let Some(suggested_ty) =
2312-
self.suggest_trait_fn_ty_for_impl_fn_infer(hir_id, None)
2313-
{
2314-
infer_replacements.push((output.span, suggested_ty.to_string()));
2315-
Ty::new_error_with_message(self.tcx(), output.span, suggested_ty.to_string())
2316-
} else {
2317-
visitor.visit_ty(output);
2318-
self.lower_ty(output)
2319-
}
2320-
}
2321-
hir::FnRetTy::DefaultReturn(..) => tcx.types.unit,
2322-
};
2279+
let (input_tys, output_ty) = self.lower_fn_sig(decl, generics, hir_id, hir_ty);
23232280

23242281
debug!(?output_ty);
23252282

23262283
let fn_ty = tcx.mk_fn_sig(input_tys, output_ty, decl.c_variadic, safety, abi);
23272284
let bare_fn_ty = ty::Binder::bind_with_vars(fn_ty, bound_vars);
23282285

2329-
if !self.allow_infer() && !(visitor.0.is_empty() && infer_replacements.is_empty()) {
2330-
// We always collect the spans for placeholder types when evaluating `fn`s, but we
2331-
// only want to emit an error complaining about them if infer types (`_`) are not
2332-
// allowed. `allow_infer` gates this behavior. We check for the presence of
2333-
// `ident_span` to not emit an error twice when we have `fn foo(_: fn() -> _)`.
2334-
2335-
let mut diag = crate::collect::placeholder_type_error_diag(
2336-
tcx,
2337-
generics,
2338-
visitor.0,
2339-
infer_replacements.iter().map(|(s, _)| *s).collect(),
2340-
true,
2341-
hir_ty,
2342-
"function",
2343-
);
2344-
2345-
if !infer_replacements.is_empty() {
2346-
diag.multipart_suggestion(
2347-
format!(
2348-
"try replacing `_` with the type{} in the corresponding trait method signature",
2349-
rustc_errors::pluralize!(infer_replacements.len()),
2350-
),
2351-
infer_replacements,
2352-
Applicability::MachineApplicable,
2353-
);
2354-
}
2355-
2356-
self.set_tainted_by_errors(diag.emit());
2357-
}
2358-
23592286
// Find any late-bound regions declared in return type that do
23602287
// not appear in the arguments. These are not well-formed.
23612288
//
@@ -2385,7 +2312,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
23852312
/// corresponding function in the trait that the impl implements, if it exists.
23862313
/// If arg_idx is Some, then it corresponds to an input type index, otherwise it
23872314
/// corresponds to the return type.
2388-
fn suggest_trait_fn_ty_for_impl_fn_infer(
2315+
pub(super) fn suggest_trait_fn_ty_for_impl_fn_infer(
23892316
&self,
23902317
fn_hir_id: HirId,
23912318
arg_idx: Option<usize>,

compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,23 @@ impl<'a, 'tcx> HirTyLowerer<'tcx> for FnCtxt<'a, 'tcx> {
355355
fn set_tainted_by_errors(&self, e: ErrorGuaranteed) {
356356
self.infcx.set_tainted_by_errors(e)
357357
}
358+
359+
fn lower_fn_sig(
360+
&self,
361+
decl: &rustc_hir::FnDecl<'tcx>,
362+
_generics: Option<&rustc_hir::Generics<'_>>,
363+
_hir_id: rustc_hir::HirId,
364+
_hir_ty: Option<&hir::Ty<'_>>,
365+
) -> (Vec<Ty<'tcx>>, Ty<'tcx>) {
366+
let tcx = self.tcx();
367+
let input_tys = decl.inputs.iter().map(|a| self.lowerer().lower_arg_ty(a, None)).collect();
368+
369+
let output_ty = match decl.output {
370+
hir::FnRetTy::Return(output) => self.lowerer().lower_ty(output),
371+
hir::FnRetTy::DefaultReturn(..) => tcx.types.unit,
372+
};
373+
(input_tys, output_ty)
374+
}
358375
}
359376

360377
/// The `ty` representation of a user-provided type. Depending on the use-site

0 commit comments

Comments
 (0)