Skip to content

Commit 840df40

Browse files
authored
Rollup merge of rust-lang#90709 - estebank:erase-known-type-params, r=nagisa
Only shown relevant type params in E0283 label When we point at a binding to suggest giving it a type, erase all the type for ADTs that have been resolved, leaving only the ones that could not be inferred. For small shallow types this is not a problem, but for big nested types with lots of params, this can otherwise cause a lot of unnecessary visual output.
2 parents f9e77f2 + 7271d1f commit 840df40

File tree

5 files changed

+201
-8
lines changed

5 files changed

+201
-8
lines changed

compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs

+131-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use crate::infer::type_variable::TypeVariableOriginKind;
2-
use crate::infer::InferCtxt;
3-
use crate::rustc_middle::ty::TypeFoldable;
2+
use crate::infer::{InferCtxt, Symbol};
43
use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder};
54
use rustc_hir as hir;
65
use rustc_hir::def::{DefKind, Namespace};
@@ -11,7 +10,7 @@ use rustc_middle::hir::map::Map;
1110
use rustc_middle::infer::unify_key::ConstVariableOriginKind;
1211
use rustc_middle::ty::print::Print;
1312
use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
14-
use rustc_middle::ty::{self, DefIdTree, InferConst, Ty, TyCtxt};
13+
use rustc_middle::ty::{self, Const, DefIdTree, InferConst, Ty, TyCtxt, TypeFoldable, TypeFolder};
1514
use rustc_span::symbol::kw;
1615
use rustc_span::Span;
1716
use std::borrow::Cow;
@@ -306,6 +305,15 @@ pub enum UnderspecifiedArgKind {
306305
Const { is_parameter: bool },
307306
}
308307

308+
impl UnderspecifiedArgKind {
309+
fn descr(&self) -> &'static str {
310+
match self {
311+
Self::Type { .. } => "type",
312+
Self::Const { .. } => "const",
313+
}
314+
}
315+
}
316+
309317
impl InferenceDiagnosticsData {
310318
/// Generate a label for a generic argument which can't be inferred. When not
311319
/// much is known about the argument, `use_diag` may be used to describe the
@@ -588,6 +596,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
588596
}
589597
}
590598

599+
let param_type = arg_data.kind.descr();
591600
let suffix = match local_visitor.found_node_ty {
592601
Some(ty) if ty.is_closure() => {
593602
let substs =
@@ -626,13 +635,15 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
626635
}
627636
Some(ty) if is_named_and_not_impl_trait(ty) && arg_data.name == "_" => {
628637
let ty = ty_to_string(ty);
629-
format!("the explicit type `{}`, with the type parameters specified", ty)
638+
format!("the explicit type `{}`, with the {} parameters specified", ty, param_type)
630639
}
631640
Some(ty) if is_named_and_not_impl_trait(ty) && ty.to_string() != arg_data.name => {
641+
let ty = ResolvedTypeParamEraser::new(self.tcx).fold_ty(ty);
642+
let ty = ErrTypeParamEraser(self.tcx).fold_ty(ty);
632643
let ty = ty_to_string(ty);
633644
format!(
634-
"the explicit type `{}`, where the type parameter `{}` is specified",
635-
ty, arg_data.name,
645+
"the explicit type `{}`, where the {} parameter `{}` is specified",
646+
ty, param_type, arg_data.name,
636647
)
637648
}
638649
_ => "a type".to_string(),
@@ -908,3 +919,117 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
908919
err
909920
}
910921
}
922+
923+
/// Turn *resolved* type params into `[type error]` to signal we don't want to display them. After
924+
/// performing that replacement, we'll turn all remaining infer type params to use their name from
925+
/// their definition, and replace all the `[type error]`s back to being infer so they display in
926+
/// the output as `_`. If we didn't go through `[type error]`, we would either show all type params
927+
/// by their name *or* `_`, neither of which is desireable: we want to show all types that we could
928+
/// infer as `_` to reduce verbosity and avoid telling the user about unnecessary type annotations.
929+
struct ResolvedTypeParamEraser<'tcx> {
930+
tcx: TyCtxt<'tcx>,
931+
level: usize,
932+
}
933+
934+
impl<'tcx> ResolvedTypeParamEraser<'tcx> {
935+
fn new(tcx: TyCtxt<'tcx>) -> Self {
936+
ResolvedTypeParamEraser { tcx, level: 0 }
937+
}
938+
939+
/// Replace not yet inferred const params with their def name.
940+
fn replace_infers(&self, c: &'tcx Const<'tcx>, index: u32, name: Symbol) -> &'tcx Const<'tcx> {
941+
match c.val {
942+
ty::ConstKind::Infer(..) => self.tcx().mk_const_param(index, name, c.ty),
943+
_ => c,
944+
}
945+
}
946+
}
947+
948+
impl<'tcx> TypeFolder<'tcx> for ResolvedTypeParamEraser<'tcx> {
949+
fn tcx<'a>(&'a self) -> TyCtxt<'tcx> {
950+
self.tcx
951+
}
952+
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
953+
self.level += 1;
954+
let t = match t.kind() {
955+
// We'll hide this type only if all its type params are hidden as well.
956+
ty::Adt(def, substs) => {
957+
let generics = self.tcx().generics_of(def.did);
958+
// Account for params with default values, like `Vec`, where we
959+
// want to show `Vec<T>`, not `Vec<T, _>`. If we replaced that
960+
// subst, then we'd get the incorrect output, so we passthrough.
961+
let substs: Vec<_> = substs
962+
.iter()
963+
.zip(generics.params.iter())
964+
.map(|(subst, param)| match &(subst.unpack(), &param.kind) {
965+
(_, ty::GenericParamDefKind::Type { has_default: true, .. }) => subst,
966+
(crate::infer::GenericArgKind::Const(c), _) => {
967+
self.replace_infers(c, param.index, param.name).into()
968+
}
969+
_ => subst.super_fold_with(self),
970+
})
971+
.collect();
972+
let should_keep = |subst: &GenericArg<'_>| match subst.unpack() {
973+
ty::subst::GenericArgKind::Type(t) => match t.kind() {
974+
ty::Error(_) => false,
975+
_ => true,
976+
},
977+
// Account for `const` params here, otherwise `doesnt_infer.rs`
978+
// shows `_` instead of `Foo<{ _: u32 }>`
979+
ty::subst::GenericArgKind::Const(_) => true,
980+
_ => false,
981+
};
982+
if self.level == 1 || substs.iter().any(should_keep) {
983+
let substs = self.tcx().intern_substs(&substs[..]);
984+
self.tcx().mk_ty(ty::Adt(def, substs))
985+
} else {
986+
self.tcx().ty_error()
987+
}
988+
}
989+
ty::Ref(_, ty, _) => {
990+
let ty = self.fold_ty(ty);
991+
match ty.kind() {
992+
// Avoid `&_`, these can be safely presented as `_`.
993+
ty::Error(_) => self.tcx().ty_error(),
994+
_ => t.super_fold_with(self),
995+
}
996+
}
997+
// We could account for `()` if we wanted to replace it, but it's assured to be short.
998+
ty::Tuple(_)
999+
| ty::Slice(_)
1000+
| ty::RawPtr(_)
1001+
| ty::FnDef(..)
1002+
| ty::FnPtr(_)
1003+
| ty::Opaque(..)
1004+
| ty::Projection(_)
1005+
| ty::Never => t.super_fold_with(self),
1006+
ty::Array(ty, c) => self
1007+
.tcx()
1008+
.mk_ty(ty::Array(self.fold_ty(ty), self.replace_infers(c, 0, Symbol::intern("N")))),
1009+
// We don't want to hide type params that haven't been resolved yet.
1010+
// This would be the type that will be written out with the type param
1011+
// name in the output.
1012+
ty::Infer(_) => t,
1013+
// We don't want to hide the outermost type, only its type params.
1014+
_ if self.level == 1 => t.super_fold_with(self),
1015+
// Hide this type
1016+
_ => self.tcx().ty_error(),
1017+
};
1018+
self.level -= 1;
1019+
t
1020+
}
1021+
}
1022+
1023+
/// Replace `[type error]` with `ty::Infer(ty::Var)` to display `_`.
1024+
struct ErrTypeParamEraser<'tcx>(TyCtxt<'tcx>);
1025+
impl<'tcx> TypeFolder<'tcx> for ErrTypeParamEraser<'tcx> {
1026+
fn tcx<'a>(&'a self) -> TyCtxt<'tcx> {
1027+
self.0
1028+
}
1029+
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
1030+
match t.kind() {
1031+
ty::Error(_) => self.tcx().mk_ty_var(ty::TyVid::from_u32(0)),
1032+
_ => t.super_fold_with(self),
1033+
}
1034+
}
1035+
}

src/test/ui/const-generics/defaults/doesnt_infer.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error[E0282]: type annotations needed for `Foo<{_: u32}>`
44
LL | let foo = Foo::foo();
55
| --- ^^^^^^^^ cannot infer the value of const parameter `N`
66
| |
7-
| consider giving `foo` the explicit type `Foo<{_: u32}>`, where the type parameter `N` is specified
7+
| consider giving `foo` the explicit type `Foo<N>`, where the const parameter `N` is specified
88

99
error: aborting due to previous error
1010

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
fn main() {
2+
let foo = foo(1, ""); //~ ERROR E0283
3+
}
4+
fn baz() {
5+
let bar = bar(1, ""); //~ ERROR E0283
6+
}
7+
8+
struct Bar<T, K, N: Default> {
9+
t: T,
10+
k: K,
11+
n: N,
12+
}
13+
14+
fn bar<T, K, Z: Default>(t: T, k: K) -> Bar<T, K, Z> {
15+
Bar { t, k, n: Default::default() }
16+
}
17+
18+
struct Foo<T, K, N: Default, M: Default> {
19+
t: T,
20+
k: K,
21+
n: N,
22+
m: M,
23+
}
24+
25+
fn foo<T, K, W: Default, Z: Default>(t: T, k: K) -> Foo<T, K, W, Z> {
26+
Foo { t, k, n: Default::default(), m: Default::default() }
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
error[E0283]: type annotations needed for `Foo<i32, &str, W, Z>`
2+
--> $DIR/erase-type-params-in-label.rs:2:15
3+
|
4+
LL | let foo = foo(1, "");
5+
| --- ^^^ cannot infer type for type parameter `W` declared on the function `foo`
6+
| |
7+
| consider giving `foo` the explicit type `Foo<_, _, W, Z>`, where the type parameter `W` is specified
8+
|
9+
= note: cannot satisfy `_: Default`
10+
note: required by a bound in `foo`
11+
--> $DIR/erase-type-params-in-label.rs:25:17
12+
|
13+
LL | fn foo<T, K, W: Default, Z: Default>(t: T, k: K) -> Foo<T, K, W, Z> {
14+
| ^^^^^^^ required by this bound in `foo`
15+
help: consider specifying the type arguments in the function call
16+
|
17+
LL | let foo = foo::<T, K, W, Z>(1, "");
18+
| ++++++++++++++
19+
20+
error[E0283]: type annotations needed for `Bar<i32, &str, Z>`
21+
--> $DIR/erase-type-params-in-label.rs:5:15
22+
|
23+
LL | let bar = bar(1, "");
24+
| --- ^^^ cannot infer type for type parameter `Z` declared on the function `bar`
25+
| |
26+
| consider giving `bar` the explicit type `Bar<_, _, Z>`, where the type parameter `Z` is specified
27+
|
28+
= note: cannot satisfy `_: Default`
29+
note: required by a bound in `bar`
30+
--> $DIR/erase-type-params-in-label.rs:14:17
31+
|
32+
LL | fn bar<T, K, Z: Default>(t: T, k: K) -> Bar<T, K, Z> {
33+
| ^^^^^^^ required by this bound in `bar`
34+
help: consider specifying the type arguments in the function call
35+
|
36+
LL | let bar = bar::<T, K, Z>(1, "");
37+
| +++++++++++
38+
39+
error: aborting due to 2 previous errors
40+
41+
For more information about this error, try `rustc --explain E0283`.

src/test/ui/inference/issue-83606.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error[E0282]: type annotations needed for `[usize; _]`
44
LL | let _ = foo("foo"); //<- Do not suggest `foo::<N>("foo");`!
55
| - ^^^ cannot infer the value of const parameter `N` declared on the function `foo`
66
| |
7-
| consider giving this pattern the explicit type `[usize; _]`, where the type parameter `N` is specified
7+
| consider giving this pattern the explicit type `[_; N]`, where the const parameter `N` is specified
88

99
error: aborting due to previous error
1010

0 commit comments

Comments
 (0)