Only collect infer vars to error about in case infer vars are actually forbidden
This commit is contained in:
parent
abd308b886
commit
6a2e15a6f0
3 changed files with 111 additions and 85 deletions
|
@ -19,10 +19,10 @@ use rustc_data_structures::captures::Captures;
|
||||||
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
|
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
|
||||||
use rustc_data_structures::unord::UnordMap;
|
use rustc_data_structures::unord::UnordMap;
|
||||||
use rustc_errors::{struct_span_code_err, Applicability, Diag, ErrorGuaranteed, StashKey, E0228};
|
use rustc_errors::{struct_span_code_err, Applicability, Diag, ErrorGuaranteed, StashKey, E0228};
|
||||||
use rustc_hir as hir;
|
|
||||||
use rustc_hir::def::DefKind;
|
use rustc_hir::def::DefKind;
|
||||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||||
use rustc_hir::intravisit::{self, Visitor};
|
use rustc_hir::intravisit::{self, walk_generics, Visitor};
|
||||||
|
use rustc_hir::{self as hir};
|
||||||
use rustc_hir::{GenericParamKind, Node};
|
use rustc_hir::{GenericParamKind, Node};
|
||||||
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
|
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
|
||||||
use rustc_infer::traits::ObligationCause;
|
use rustc_infer::traits::ObligationCause;
|
||||||
|
@ -529,6 +529,89 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> {
|
||||||
fn set_tainted_by_errors(&self, err: ErrorGuaranteed) {
|
fn set_tainted_by_errors(&self, err: ErrorGuaranteed) {
|
||||||
self.tainted_by_errors.set(Some(err));
|
self.tainted_by_errors.set(Some(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn lower_fn_sig(
|
||||||
|
&self,
|
||||||
|
decl: &hir::FnDecl<'tcx>,
|
||||||
|
generics: Option<&hir::Generics<'_>>,
|
||||||
|
hir_id: rustc_hir::HirId,
|
||||||
|
hir_ty: Option<&hir::Ty<'_>>,
|
||||||
|
) -> (Vec<Ty<'tcx>>, Ty<'tcx>) {
|
||||||
|
let tcx = self.tcx();
|
||||||
|
// We proactively collect all the inferred type params to emit a single error per fn def.
|
||||||
|
let mut visitor = HirPlaceholderCollector::default();
|
||||||
|
let mut infer_replacements = vec![];
|
||||||
|
|
||||||
|
if let Some(generics) = generics {
|
||||||
|
walk_generics(&mut visitor, generics);
|
||||||
|
}
|
||||||
|
|
||||||
|
let input_tys = decl
|
||||||
|
.inputs
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, a)| {
|
||||||
|
if let hir::TyKind::Infer = a.kind {
|
||||||
|
if let Some(suggested_ty) =
|
||||||
|
self.lowerer().suggest_trait_fn_ty_for_impl_fn_infer(hir_id, Some(i))
|
||||||
|
{
|
||||||
|
infer_replacements.push((a.span, suggested_ty.to_string()));
|
||||||
|
return Ty::new_error_with_message(tcx, a.span, suggested_ty.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only visit the type looking for `_` if we didn't fix the type above
|
||||||
|
visitor.visit_ty(a);
|
||||||
|
self.lowerer().lower_arg_ty(a, None)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let output_ty = match decl.output {
|
||||||
|
hir::FnRetTy::Return(output) => {
|
||||||
|
if let hir::TyKind::Infer = output.kind
|
||||||
|
&& let Some(suggested_ty) =
|
||||||
|
self.lowerer().suggest_trait_fn_ty_for_impl_fn_infer(hir_id, None)
|
||||||
|
{
|
||||||
|
infer_replacements.push((output.span, suggested_ty.to_string()));
|
||||||
|
Ty::new_error_with_message(tcx, output.span, suggested_ty.to_string())
|
||||||
|
} else {
|
||||||
|
visitor.visit_ty(output);
|
||||||
|
self.lower_ty(output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hir::FnRetTy::DefaultReturn(..) => tcx.types.unit,
|
||||||
|
};
|
||||||
|
|
||||||
|
if !(visitor.0.is_empty() && infer_replacements.is_empty()) {
|
||||||
|
// We check for the presence of
|
||||||
|
// `ident_span` to not emit an error twice when we have `fn foo(_: fn() -> _)`.
|
||||||
|
|
||||||
|
let mut diag = crate::collect::placeholder_type_error_diag(
|
||||||
|
tcx,
|
||||||
|
generics,
|
||||||
|
visitor.0,
|
||||||
|
infer_replacements.iter().map(|(s, _)| *s).collect(),
|
||||||
|
true,
|
||||||
|
hir_ty,
|
||||||
|
"function",
|
||||||
|
);
|
||||||
|
|
||||||
|
if !infer_replacements.is_empty() {
|
||||||
|
diag.multipart_suggestion(
|
||||||
|
format!(
|
||||||
|
"try replacing `_` with the type{} in the corresponding trait method signature",
|
||||||
|
rustc_errors::pluralize!(infer_replacements.len()),
|
||||||
|
),
|
||||||
|
infer_replacements,
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.set_tainted_by_errors(diag.emit());
|
||||||
|
}
|
||||||
|
|
||||||
|
(input_tys, output_ty)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Synthesize a new lifetime name that doesn't clash with any of the lifetimes already present.
|
/// Synthesize a new lifetime name that doesn't clash with any of the lifetimes already present.
|
||||||
|
|
|
@ -20,7 +20,6 @@ mod lint;
|
||||||
mod object_safety;
|
mod object_safety;
|
||||||
|
|
||||||
use crate::bounds::Bounds;
|
use crate::bounds::Bounds;
|
||||||
use crate::collect::HirPlaceholderCollector;
|
|
||||||
use crate::errors::{AmbiguousLifetimeBound, WildPatTy};
|
use crate::errors::{AmbiguousLifetimeBound, WildPatTy};
|
||||||
use crate::hir_ty_lowering::errors::{prohibit_assoc_item_constraint, GenericsArgsErrExtend};
|
use crate::hir_ty_lowering::errors::{prohibit_assoc_item_constraint, GenericsArgsErrExtend};
|
||||||
use crate::hir_ty_lowering::generics::{check_generic_arg_count, lower_generic_args};
|
use crate::hir_ty_lowering::generics::{check_generic_arg_count, lower_generic_args};
|
||||||
|
@ -34,7 +33,6 @@ use rustc_errors::{
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def::{CtorOf, DefKind, Namespace, Res};
|
use rustc_hir::def::{CtorOf, DefKind, Namespace, Res};
|
||||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||||
use rustc_hir::intravisit::{walk_generics, Visitor as _};
|
|
||||||
use rustc_hir::{GenericArg, GenericArgs, HirId};
|
use rustc_hir::{GenericArg, GenericArgs, HirId};
|
||||||
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
|
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
|
||||||
use rustc_infer::traits::ObligationCause;
|
use rustc_infer::traits::ObligationCause;
|
||||||
|
@ -157,6 +155,14 @@ pub trait HirTyLowerer<'tcx> {
|
||||||
poly_trait_ref: ty::PolyTraitRef<'tcx>,
|
poly_trait_ref: ty::PolyTraitRef<'tcx>,
|
||||||
) -> Ty<'tcx>;
|
) -> Ty<'tcx>;
|
||||||
|
|
||||||
|
fn lower_fn_sig(
|
||||||
|
&self,
|
||||||
|
decl: &hir::FnDecl<'tcx>,
|
||||||
|
generics: Option<&hir::Generics<'_>>,
|
||||||
|
hir_id: HirId,
|
||||||
|
hir_ty: Option<&hir::Ty<'_>>,
|
||||||
|
) -> (Vec<Ty<'tcx>>, Ty<'tcx>);
|
||||||
|
|
||||||
/// Returns `AdtDef` if `ty` is an ADT.
|
/// Returns `AdtDef` if `ty` is an ADT.
|
||||||
///
|
///
|
||||||
/// Note that `ty` might be a alias type that needs normalization.
|
/// Note that `ty` might be a alias type that needs normalization.
|
||||||
|
@ -2306,92 +2312,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
||||||
let bound_vars = tcx.late_bound_vars(hir_id);
|
let bound_vars = tcx.late_bound_vars(hir_id);
|
||||||
debug!(?bound_vars);
|
debug!(?bound_vars);
|
||||||
|
|
||||||
// We proactively collect all the inferred type params to emit a single error per fn def.
|
let (input_tys, output_ty) = self.lower_fn_sig(decl, generics, hir_id, hir_ty);
|
||||||
let mut visitor = HirPlaceholderCollector::default();
|
|
||||||
let mut infer_replacements = vec![];
|
|
||||||
|
|
||||||
if let Some(generics) = generics {
|
|
||||||
walk_generics(&mut visitor, generics);
|
|
||||||
}
|
|
||||||
|
|
||||||
let input_tys: Vec<_> = decl
|
|
||||||
.inputs
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(i, a)| {
|
|
||||||
if let hir::TyKind::Infer = a.kind
|
|
||||||
&& !self.allow_infer()
|
|
||||||
{
|
|
||||||
if let Some(suggested_ty) =
|
|
||||||
self.suggest_trait_fn_ty_for_impl_fn_infer(hir_id, Some(i))
|
|
||||||
{
|
|
||||||
infer_replacements.push((a.span, suggested_ty.to_string()));
|
|
||||||
return Ty::new_error_with_message(
|
|
||||||
self.tcx(),
|
|
||||||
a.span,
|
|
||||||
suggested_ty.to_string(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only visit the type looking for `_` if we didn't fix the type above
|
|
||||||
visitor.visit_ty(a);
|
|
||||||
self.lower_arg_ty(a, None)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let output_ty = match decl.output {
|
|
||||||
hir::FnRetTy::Return(output) => {
|
|
||||||
if let hir::TyKind::Infer = output.kind
|
|
||||||
&& !self.allow_infer()
|
|
||||||
&& let Some(suggested_ty) =
|
|
||||||
self.suggest_trait_fn_ty_for_impl_fn_infer(hir_id, None)
|
|
||||||
{
|
|
||||||
infer_replacements.push((output.span, suggested_ty.to_string()));
|
|
||||||
Ty::new_error_with_message(self.tcx(), output.span, suggested_ty.to_string())
|
|
||||||
} else {
|
|
||||||
visitor.visit_ty(output);
|
|
||||||
self.lower_ty(output)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
hir::FnRetTy::DefaultReturn(..) => tcx.types.unit,
|
|
||||||
};
|
|
||||||
|
|
||||||
debug!(?output_ty);
|
debug!(?output_ty);
|
||||||
|
|
||||||
let fn_ty = tcx.mk_fn_sig(input_tys, output_ty, decl.c_variadic, safety, abi);
|
let fn_ty = tcx.mk_fn_sig(input_tys, output_ty, decl.c_variadic, safety, abi);
|
||||||
let bare_fn_ty = ty::Binder::bind_with_vars(fn_ty, bound_vars);
|
let bare_fn_ty = ty::Binder::bind_with_vars(fn_ty, bound_vars);
|
||||||
|
|
||||||
if !self.allow_infer() && !(visitor.0.is_empty() && infer_replacements.is_empty()) {
|
|
||||||
// We always collect the spans for placeholder types when evaluating `fn`s, but we
|
|
||||||
// only want to emit an error complaining about them if infer types (`_`) are not
|
|
||||||
// allowed. `allow_infer` gates this behavior. We check for the presence of
|
|
||||||
// `ident_span` to not emit an error twice when we have `fn foo(_: fn() -> _)`.
|
|
||||||
|
|
||||||
let mut diag = crate::collect::placeholder_type_error_diag(
|
|
||||||
tcx,
|
|
||||||
generics,
|
|
||||||
visitor.0,
|
|
||||||
infer_replacements.iter().map(|(s, _)| *s).collect(),
|
|
||||||
true,
|
|
||||||
hir_ty,
|
|
||||||
"function",
|
|
||||||
);
|
|
||||||
|
|
||||||
if !infer_replacements.is_empty() {
|
|
||||||
diag.multipart_suggestion(
|
|
||||||
format!(
|
|
||||||
"try replacing `_` with the type{} in the corresponding trait method signature",
|
|
||||||
rustc_errors::pluralize!(infer_replacements.len()),
|
|
||||||
),
|
|
||||||
infer_replacements,
|
|
||||||
Applicability::MachineApplicable,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.set_tainted_by_errors(diag.emit());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find any late-bound regions declared in return type that do
|
// Find any late-bound regions declared in return type that do
|
||||||
// not appear in the arguments. These are not well-formed.
|
// not appear in the arguments. These are not well-formed.
|
||||||
//
|
//
|
||||||
|
@ -2421,7 +2348,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
||||||
/// corresponding function in the trait that the impl implements, if it exists.
|
/// corresponding function in the trait that the impl implements, if it exists.
|
||||||
/// If arg_idx is Some, then it corresponds to an input type index, otherwise it
|
/// If arg_idx is Some, then it corresponds to an input type index, otherwise it
|
||||||
/// corresponds to the return type.
|
/// corresponds to the return type.
|
||||||
fn suggest_trait_fn_ty_for_impl_fn_infer(
|
pub(super) fn suggest_trait_fn_ty_for_impl_fn_infer(
|
||||||
&self,
|
&self,
|
||||||
fn_hir_id: HirId,
|
fn_hir_id: HirId,
|
||||||
arg_idx: Option<usize>,
|
arg_idx: Option<usize>,
|
||||||
|
|
|
@ -355,6 +355,22 @@ impl<'a, 'tcx> HirTyLowerer<'tcx> for FnCtxt<'a, 'tcx> {
|
||||||
fn set_tainted_by_errors(&self, e: ErrorGuaranteed) {
|
fn set_tainted_by_errors(&self, e: ErrorGuaranteed) {
|
||||||
self.infcx.set_tainted_by_errors(e)
|
self.infcx.set_tainted_by_errors(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn lower_fn_sig(
|
||||||
|
&self,
|
||||||
|
decl: &rustc_hir::FnDecl<'tcx>,
|
||||||
|
_generics: Option<&rustc_hir::Generics<'_>>,
|
||||||
|
_hir_id: rustc_hir::HirId,
|
||||||
|
_hir_ty: Option<&hir::Ty<'_>>,
|
||||||
|
) -> (Vec<Ty<'tcx>>, Ty<'tcx>) {
|
||||||
|
let input_tys = decl.inputs.iter().map(|a| self.lowerer().lower_arg_ty(a, None)).collect();
|
||||||
|
|
||||||
|
let output_ty = match decl.output {
|
||||||
|
hir::FnRetTy::Return(output) => self.lowerer().lower_ty(output),
|
||||||
|
hir::FnRetTy::DefaultReturn(..) => self.tcx().types.unit,
|
||||||
|
};
|
||||||
|
(input_tys, output_ty)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The `ty` representation of a user-provided type. Depending on the use-site
|
/// The `ty` representation of a user-provided type. Depending on the use-site
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue