diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs index 56f0b5c1927..739987e850a 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs @@ -10,7 +10,7 @@ use rustc_middle::hir::map::Map; use rustc_middle::infer::unify_key::ConstVariableOriginKind; use rustc_middle::ty::print::Print; use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; -use rustc_middle::ty::{self, DefIdTree, InferConst, Ty, TyCtxt, TypeFoldable, TypeFolder}; +use rustc_middle::ty::{self, Const, DefIdTree, InferConst, Ty, TyCtxt, TypeFoldable, TypeFolder}; use rustc_span::symbol::kw; use rustc_span::Span; use std::borrow::Cow; @@ -305,6 +305,15 @@ pub enum UnderspecifiedArgKind { Const { is_parameter: bool }, } +impl UnderspecifiedArgKind { + fn descr(&self) -> &'static str { + match self { + Self::Type { .. } => "type", + Self::Const { .. } => "const", + } + } +} + impl InferenceDiagnosticsData { /// Generate a label for a generic argument which can't be inferred. When not /// much is known about the argument, `use_diag` may be used to describe the @@ -587,6 +596,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } + let param_type = arg_data.kind.descr(); let suffix = match local_visitor.found_node_ty { Some(ty) if ty.is_closure() => { let substs = @@ -625,15 +635,15 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } Some(ty) if is_named_and_not_impl_trait(ty) && arg_data.name == "_" => { let ty = ty_to_string(ty); - format!("the explicit type `{}`, with the type parameters specified", ty) + format!("the explicit type `{}`, with the {} parameters specified", ty, param_type) } Some(ty) if is_named_and_not_impl_trait(ty) && ty.to_string() != arg_data.name => { let ty = ResolvedTypeParamEraser::new(self.tcx).fold_ty(ty); let ty = ErrTypeParamEraser(self.tcx).fold_ty(ty); let ty = ty_to_string(ty); format!( - "the explicit type `{}`, where the type parameter `{}` is specified", - ty, arg_data.name, + "the explicit type `{}`, where the {} parameter `{}` is specified", + ty, param_type, arg_data.name, ) } _ => "a type".to_string(), @@ -910,7 +920,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } -/// Turn resolved type params into `[type error]` to signal we don't want to display them. +/// Turn *resolved* type params into `[type error]` to signal we don't want to display them. After +/// performing that replacement, we'll turn all remaining infer type params to use their name from +/// their definition, and replace all the `[type error]`s back to being infer so they display in +/// the output as `_`. If we didn't go through `[type error]`, we would either show all type params +/// by their name *or* `_`, neither of which is desireable: we want to show all types that we could +/// infer as `_` to reduce verbosity and avoid telling the user about unnecessary type annotations. struct ResolvedTypeParamEraser<'tcx> { tcx: TyCtxt<'tcx>, level: usize, @@ -920,7 +935,18 @@ impl<'tcx> ResolvedTypeParamEraser<'tcx> { fn new(tcx: TyCtxt<'tcx>) -> Self { ResolvedTypeParamEraser { tcx, level: 0 } } + + /// Replace not yet inferred const params with their def name. + fn replace_infers(&self, c: &'tcx Const<'tcx>, index: u32, name: Symbol) -> &'tcx Const<'tcx> { + match c.val { + ty::ConstKind::Infer(..) => { + self.tcx().mk_const_param(index, name, c.ty) + } + _ => c, + } + } } + impl<'tcx> TypeFolder<'tcx> for ResolvedTypeParamEraser<'tcx> { fn tcx<'a>(&'a self) -> TyCtxt<'tcx> { self.tcx @@ -940,29 +966,22 @@ impl<'tcx> TypeFolder<'tcx> for ResolvedTypeParamEraser<'tcx> { .map(|(subst, param)| match &(subst.unpack(), ¶m.kind) { (_, ty::GenericParamDefKind::Type { has_default: true, .. }) => subst, (crate::infer::GenericArgKind::Const(c), _) => { - match c.val { - ty::ConstKind::Infer(..) => { - // Replace not yet inferred const params with their def name. - self.tcx().mk_const_param(param.index, param.name, c.ty).into() - } - _ => subst, - } + self.replace_infers(c, param.index, param.name).into() } _ => subst.super_fold_with(self), }) .collect(); - if self.level == 1 - || substs.iter().any(|subst| match subst.unpack() { - ty::subst::GenericArgKind::Type(t) => match t.kind() { - ty::Error(_) => false, - _ => true, - }, - // Account for `const` params here, otherwise `doesnt_infer.rs` - // shows `_` instead of `Foo<{ _: u32 }>` - ty::subst::GenericArgKind::Const(_) => true, - _ => false, - }) - { + let should_keep = |subst: &GenericArg<'_>| match subst.unpack() { + ty::subst::GenericArgKind::Type(t) => match t.kind() { + ty::Error(_) => false, + _ => true, + }, + // Account for `const` params here, otherwise `doesnt_infer.rs` + // shows `_` instead of `Foo<{ _: u32 }>` + ty::subst::GenericArgKind::Const(_) => true, + _ => false, + }; + if self.level == 1 || substs.iter().any(should_keep) { let substs = self.tcx().intern_substs(&substs[..]); self.tcx().mk_ty(ty::Adt(def, substs)) } else { @@ -986,18 +1005,9 @@ impl<'tcx> TypeFolder<'tcx> for ResolvedTypeParamEraser<'tcx> { | ty::Opaque(..) | ty::Projection(_) | ty::Never => t.super_fold_with(self), - ty::Array(ty, c) => { - self.tcx().mk_ty(ty::Array( - self.fold_ty(ty), - match c.val { - ty::ConstKind::Infer(..) => { - // Replace not yet inferred const params with their def name. - self.tcx().mk_const_param(0, Symbol::intern("N"), c.ty).into() - } - _ => c, - }, - )) - } + ty::Array(ty, c) => self + .tcx() + .mk_ty(ty::Array(self.fold_ty(ty), self.replace_infers(c, 0, Symbol::intern("N")))), // We don't want to hide type params that haven't been resolved yet. // This would be the type that will be written out with the type param // name in the output. diff --git a/src/test/ui/const-generics/defaults/doesnt_infer.stderr b/src/test/ui/const-generics/defaults/doesnt_infer.stderr index 10cd491b480..d6c64d58be5 100644 --- a/src/test/ui/const-generics/defaults/doesnt_infer.stderr +++ b/src/test/ui/const-generics/defaults/doesnt_infer.stderr @@ -4,7 +4,7 @@ error[E0282]: type annotations needed for `Foo<{_: u32}>` LL | let foo = Foo::foo(); | --- ^^^^^^^^ cannot infer the value of const parameter `N` | | - | consider giving `foo` the explicit type `Foo`, where the type parameter `N` is specified + | consider giving `foo` the explicit type `Foo`, where the const parameter `N` is specified error: aborting due to previous error diff --git a/src/test/ui/inference/issue-83606.stderr b/src/test/ui/inference/issue-83606.stderr index 0746b2491fb..9ca8f35fd54 100644 --- a/src/test/ui/inference/issue-83606.stderr +++ b/src/test/ui/inference/issue-83606.stderr @@ -4,7 +4,7 @@ error[E0282]: type annotations needed for `[usize; _]` LL | let _ = foo("foo"); //<- Do not suggest `foo::("foo");`! | - ^^^ cannot infer the value of const parameter `N` declared on the function `foo` | | - | consider giving this pattern the explicit type `[_; N]`, where the type parameter `N` is specified + | consider giving this pattern the explicit type `[_; N]`, where the const parameter `N` is specified error: aborting due to previous error