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 32c02033dc9b9..9cf6cde259150 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 @@ -1,6 +1,5 @@ use crate::infer::type_variable::TypeVariableOriginKind; -use crate::infer::InferCtxt; -use crate::rustc_middle::ty::TypeFoldable; +use crate::infer::{InferCtxt, Symbol}; use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Namespace}; @@ -11,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}; +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; @@ -306,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 @@ -588,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 = @@ -626,13 +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(), @@ -908,3 +919,117 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { err } } + +/// 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, +} + +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 + } + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + self.level += 1; + let t = match t.kind() { + // We'll hide this type only if all its type params are hidden as well. + ty::Adt(def, substs) => { + let generics = self.tcx().generics_of(def.did); + // Account for params with default values, like `Vec`, where we + // want to show `Vec`, not `Vec`. If we replaced that + // subst, then we'd get the incorrect output, so we passthrough. + let substs: Vec<_> = substs + .iter() + .zip(generics.params.iter()) + .map(|(subst, param)| match &(subst.unpack(), ¶m.kind) { + (_, ty::GenericParamDefKind::Type { has_default: true, .. }) => subst, + (crate::infer::GenericArgKind::Const(c), _) => { + self.replace_infers(c, param.index, param.name).into() + } + _ => subst.super_fold_with(self), + }) + .collect(); + 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 { + self.tcx().ty_error() + } + } + ty::Ref(_, ty, _) => { + let ty = self.fold_ty(ty); + match ty.kind() { + // Avoid `&_`, these can be safely presented as `_`. + ty::Error(_) => self.tcx().ty_error(), + _ => t.super_fold_with(self), + } + } + // We could account for `()` if we wanted to replace it, but it's assured to be short. + ty::Tuple(_) + | ty::Slice(_) + | ty::RawPtr(_) + | ty::FnDef(..) + | ty::FnPtr(_) + | 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), 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. + ty::Infer(_) => t, + // We don't want to hide the outermost type, only its type params. + _ if self.level == 1 => t.super_fold_with(self), + // Hide this type + _ => self.tcx().ty_error(), + }; + self.level -= 1; + t + } +} + +/// Replace `[type error]` with `ty::Infer(ty::Var)` to display `_`. +struct ErrTypeParamEraser<'tcx>(TyCtxt<'tcx>); +impl<'tcx> TypeFolder<'tcx> for ErrTypeParamEraser<'tcx> { + fn tcx<'a>(&'a self) -> TyCtxt<'tcx> { + self.0 + } + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + match t.kind() { + ty::Error(_) => self.tcx().mk_ty_var(ty::TyVid::from_u32(0)), + _ => t.super_fold_with(self), + } + } +} diff --git a/src/test/ui/const-generics/defaults/doesnt_infer.stderr b/src/test/ui/const-generics/defaults/doesnt_infer.stderr index b57975e26f290..d6c64d58be5f3 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<{_: u32}>`, 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/erase-type-params-in-label.rs b/src/test/ui/inference/erase-type-params-in-label.rs new file mode 100644 index 0000000000000..1fea2da92da94 --- /dev/null +++ b/src/test/ui/inference/erase-type-params-in-label.rs @@ -0,0 +1,27 @@ +fn main() { + let foo = foo(1, ""); //~ ERROR E0283 +} +fn baz() { + let bar = bar(1, ""); //~ ERROR E0283 +} + +struct Bar { + t: T, + k: K, + n: N, +} + +fn bar(t: T, k: K) -> Bar { + Bar { t, k, n: Default::default() } +} + +struct Foo { + t: T, + k: K, + n: N, + m: M, +} + +fn foo(t: T, k: K) -> Foo { + Foo { t, k, n: Default::default(), m: Default::default() } +} diff --git a/src/test/ui/inference/erase-type-params-in-label.stderr b/src/test/ui/inference/erase-type-params-in-label.stderr new file mode 100644 index 0000000000000..d0b06cde9d63a --- /dev/null +++ b/src/test/ui/inference/erase-type-params-in-label.stderr @@ -0,0 +1,41 @@ +error[E0283]: type annotations needed for `Foo` + --> $DIR/erase-type-params-in-label.rs:2:15 + | +LL | let foo = foo(1, ""); + | --- ^^^ cannot infer type for type parameter `W` declared on the function `foo` + | | + | consider giving `foo` the explicit type `Foo<_, _, W, Z>`, where the type parameter `W` is specified + | + = note: cannot satisfy `_: Default` +note: required by a bound in `foo` + --> $DIR/erase-type-params-in-label.rs:25:17 + | +LL | fn foo(t: T, k: K) -> Foo { + | ^^^^^^^ required by this bound in `foo` +help: consider specifying the type arguments in the function call + | +LL | let foo = foo::(1, ""); + | ++++++++++++++ + +error[E0283]: type annotations needed for `Bar` + --> $DIR/erase-type-params-in-label.rs:5:15 + | +LL | let bar = bar(1, ""); + | --- ^^^ cannot infer type for type parameter `Z` declared on the function `bar` + | | + | consider giving `bar` the explicit type `Bar<_, _, Z>`, where the type parameter `Z` is specified + | + = note: cannot satisfy `_: Default` +note: required by a bound in `bar` + --> $DIR/erase-type-params-in-label.rs:14:17 + | +LL | fn bar(t: T, k: K) -> Bar { + | ^^^^^^^ required by this bound in `bar` +help: consider specifying the type arguments in the function call + | +LL | let bar = bar::(1, ""); + | +++++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0283`. diff --git a/src/test/ui/inference/issue-83606.stderr b/src/test/ui/inference/issue-83606.stderr index 65f3336b9358a..9ca8f35fd545a 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 `[usize; _]`, 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