Skip to content

Commit 915c27c

Browse files
committed
In "specify type" suggestion, skip type params that are already known
When we suggest specifying a type for an expression or pattern, like in a `let` binding, we previously would print the entire type as the type system knew it. We now look at the params that have *no* inference variables, so they are fully known to the type system which means that they don't need to be specified. This helps in suggestions for types that are really long, because we can usually skip most of the type params and make the annotation as short as possible: ``` error[E0282]: type annotations needed for `Result<_, ((..., ..., ..., ...), ..., ..., ...)>` --> $DIR/really-long-type-in-let-binding-without-sufficient-type-info.rs:7:9 | LL | let y = Err(x); | ^ ------ type must be known at this point | help: consider giving `y` an explicit type, where the type for type parameter `T` is specified | LL | let y: Result<T, _> = Err(x); | ++++++++++++++ ```
1 parent 99768c8 commit 915c27c

8 files changed

+81
-20
lines changed

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

+45-8
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use rustc_middle::ty::{
1818
TypeFoldable, TypeFolder, TypeSuperFoldable, TypeckResults,
1919
};
2020
use rustc_span::{BytePos, DUMMY_SP, FileName, Ident, Span, sym};
21+
use rustc_type_ir::visit::TypeVisitableExt;
2122
use tracing::{debug, instrument, warn};
2223

2324
use super::nice_region_error::placeholder_error::Highlighted;
@@ -157,34 +158,70 @@ impl UnderspecifiedArgKind {
157158

158159
struct ClosureEraser<'tcx> {
159160
tcx: TyCtxt<'tcx>,
161+
skip: bool,
160162
}
161163

164+
use rustc_type_ir::inherent::*;
162165
impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ClosureEraser<'tcx> {
163166
fn cx(&self) -> TyCtxt<'tcx> {
164167
self.tcx
165168
}
166169

167170
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
168-
match ty.kind() {
171+
let prev = self.skip;
172+
let ty = match ty.kind() {
169173
ty::Closure(_, args) => {
170174
let closure_sig = args.as_closure().sig();
171175
Ty::new_fn_ptr(
172176
self.tcx,
173177
self.tcx.signature_unclosure(closure_sig, hir::Safety::Safe),
174178
)
175179
}
176-
_ => ty.super_fold_with(self),
177-
}
180+
ty::Adt(def, _) => {
181+
let generics = self.tcx.generics_of(def.def_id());
182+
let ty = if generics
183+
.own_params
184+
.iter()
185+
.any(|param| param.default_value(self.tcx).is_some())
186+
{
187+
self.skip = true;
188+
let ty = ty.super_fold_with(self);
189+
ty
190+
} else if ty.has_infer() || self.skip {
191+
ty.super_fold_with(self)
192+
} else {
193+
// When we have a type that doesn't have any inference variables (or default
194+
// types, like the allocator in Vec), we replace the whole thing with `_`,
195+
// because the type system already knows about that type and it is redundant to
196+
// specify it. The user only needs to specify the type parameters that we
197+
// *couldn't* figure out.
198+
self.tcx.mk_ty_from_kind(ty::Infer(ty::TyVar(ty::TyVid::ZERO)))
199+
};
200+
ty
201+
}
202+
_ if ty.has_infer() || self.skip => {
203+
self.skip = false;
204+
ty.super_fold_with(self)
205+
}
206+
_ => self.tcx.mk_ty_from_kind(ty::Infer(ty::TyVar(ty::TyVid::ZERO))),
207+
};
208+
self.skip = prev;
209+
ty
210+
}
211+
212+
fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> {
213+
let prev = self.skip;
214+
// Avoid accidentally erasing the type of the const.
215+
self.skip = true;
216+
let c = c.super_fold_with(self);
217+
self.skip = prev;
218+
c
178219
}
179220
}
180221

181222
fn fmt_printer<'a, 'tcx>(infcx: &'a InferCtxt<'tcx>, ns: Namespace) -> FmtPrinter<'a, 'tcx> {
182223
let mut printer = FmtPrinter::new(infcx.tcx, ns);
183224
let ty_getter = move |ty_vid| {
184-
if infcx.probe_ty_var(ty_vid).is_ok() {
185-
warn!("resolved ty var in error message");
186-
}
187-
188225
let var_origin = infcx.type_var_origin(ty_vid);
189226
if let Some(def_id) = var_origin.param_def_id
190227
// The `Self` param of a trait has the def-id of the trait,
@@ -221,7 +258,7 @@ fn ty_to_string<'tcx>(
221258
let ty = infcx.resolve_vars_if_possible(ty);
222259
// We use `fn` ptr syntax for closures, but this only works when the closure
223260
// does not capture anything.
224-
let ty = ty.fold_with(&mut ClosureEraser { tcx: infcx.tcx });
261+
let ty = ty.fold_with(&mut ClosureEraser { tcx: infcx.tcx, skip: false });
225262

226263
match (ty.kind(), called_method_def_id) {
227264
// We don't want the regular output for `fn`s because it includes its path in

tests/ui/inference/cannot-infer-closure-circular.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ LL | Ok(v)
99
|
1010
help: consider giving this closure parameter an explicit type, where the type for type parameter `E` is specified
1111
|
12-
LL | let x = |r: Result<(), E>| {
13-
| +++++++++++++++
12+
LL | let x = |r: Result<_, E>| {
13+
| ++++++++++++++
1414

1515
error: aborting due to 1 previous error
1616

tests/ui/inference/erase-type-params-in-label.stderr

+4-4
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ LL | fn foo<T, K, W: Default, Z: Default>(t: T, k: K) -> Foo<T, K, W, Z> {
1212
| ^^^^^^^ required by this bound in `foo`
1313
help: consider giving `foo` an explicit type, where the type for type parameter `W` is specified
1414
|
15-
LL | let foo: Foo<i32, &str, W, Z> = foo(1, "");
16-
| ++++++++++++++++++++++
15+
LL | let foo: Foo<_, &_, W, Z> = foo(1, "");
16+
| ++++++++++++++++++
1717

1818
error[E0283]: type annotations needed for `Bar<i32, &str, _>`
1919
--> $DIR/erase-type-params-in-label.rs:5:9
@@ -29,8 +29,8 @@ LL | fn bar<T, K, Z: Default>(t: T, k: K) -> Bar<T, K, Z> {
2929
| ^^^^^^^ required by this bound in `bar`
3030
help: consider giving `bar` an explicit type, where the type for type parameter `Z` is specified
3131
|
32-
LL | let bar: Bar<i32, &str, Z> = bar(1, "");
33-
| +++++++++++++++++++
32+
LL | let bar: Bar<_, &_, Z> = bar(1, "");
33+
| +++++++++++++++
3434

3535
error: aborting due to 2 previous errors
3636

tests/ui/inference/issue-104649.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ LL | let a = A(Result::Ok(Result::Ok(())));
66
|
77
help: consider giving `a` an explicit type, where the type for type parameter `E` is specified
88
|
9-
LL | let a: A<std::result::Result<std::result::Result<(), E>, Error>> = A(Result::Ok(Result::Ok(())));
10-
| +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
9+
LL | let a: A<std::result::Result<std::result::Result<_, E>, _>> = A(Result::Ok(Result::Ok(())));
10+
| ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1111

1212
error: aborting due to 1 previous error
1313

tests/ui/inference/issue-83606.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ LL | fn foo<const N: usize>(_: impl std::fmt::Display) -> [usize; N] {
1111
| ^^^^^^^^^^^^^^ required by this const generic parameter in `foo`
1212
help: consider giving this pattern a type, where the value of const parameter `N` is specified
1313
|
14-
LL | let _: [usize; N] = foo("foo");
15-
| ++++++++++++
14+
LL | let _: [_; N] = foo("foo");
15+
| ++++++++
1616

1717
error: aborting due to 1 previous error
1818

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
type A = (i32, i32, i32, i32);
2+
type B = (A, A, A, A);
3+
type C = (B, B, B, B);
4+
type D = (C, C, C, C);
5+
6+
fn foo(x: D) {
7+
let y = Err(x); //~ ERROR type annotations needed for `Result<_
8+
}
9+
10+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error[E0282]: type annotations needed for `Result<_, ((((i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32)), ((i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32)), ((i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32)), ((i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32))), (((i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32)), ((i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32)), ((i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32)), ((i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32))), (((i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32)), ((i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32)), ((i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32)), ((i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32))), (((i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32)), ((i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32)), ((i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32)), ((i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32))))>`
2+
--> $DIR/really-long-type-in-let-binding-without-sufficient-type-info.rs:7:9
3+
|
4+
LL | let y = Err(x);
5+
| ^ ------ type must be known at this point
6+
|
7+
help: consider giving `y` an explicit type, where the type for type parameter `T` is specified
8+
|
9+
LL | let y: Result<T, _> = Err(x);
10+
| ++++++++++++++
11+
12+
error: aborting due to 1 previous error
13+
14+
For more information about this error, try `rustc --explain E0282`.

tests/ui/type-inference/or_else-multiple-type-params.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ LL | .or_else(|err| {
66
|
77
help: try giving this closure an explicit return type
88
|
9-
LL | .or_else(|err| -> Result<Child, F> {
10-
| +++++++++++++++++++
9+
LL | .or_else(|err| -> Result<_, F> {
10+
| +++++++++++++++
1111

1212
error: aborting due to 1 previous error
1313

0 commit comments

Comments
 (0)