Skip to content

Commit 7fa8a55

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 2f348cb commit 7fa8a55

8 files changed

+111
-28
lines changed

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

+75-16
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;
@@ -155,36 +156,94 @@ impl UnderspecifiedArgKind {
155156
}
156157
}
157158

158-
struct ClosureEraser<'tcx> {
159-
tcx: TyCtxt<'tcx>,
159+
struct ClosureEraser<'a, 'tcx> {
160+
infcx: &'a InferCtxt<'tcx>,
161+
// When recursing into types, if an ADT has type parameters with a default type we do *not*
162+
// want to replace that type parameter with `_`, as it will cause the normally hidden type
163+
// parameter to be rendered. The best example of this is `Vec<T, Alloc>`, which we want to
164+
// render as `Vec<T>` and not `Vec<T, _>` when `T` is unknown.
165+
do_not_hide_nested_type: bool,
160166
}
161167

162-
impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ClosureEraser<'tcx> {
168+
impl<'a, 'tcx> ClosureEraser<'a, 'tcx> {
169+
fn new_infer(&mut self) -> Ty<'tcx> {
170+
self.infcx.next_ty_var(DUMMY_SP)
171+
}
172+
}
173+
174+
impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for ClosureEraser<'a, 'tcx> {
163175
fn cx(&self) -> TyCtxt<'tcx> {
164-
self.tcx
176+
self.infcx.tcx
165177
}
166178

167179
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
168-
match ty.kind() {
180+
let prev = self.do_not_hide_nested_type;
181+
let ty = match ty.kind() {
169182
ty::Closure(_, args) => {
183+
// For a closure type, we turn it into a function pointer so that it gets rendered
184+
// as `fn(args) -> Ret`.
170185
let closure_sig = args.as_closure().sig();
171186
Ty::new_fn_ptr(
172-
self.tcx,
173-
self.tcx.signature_unclosure(closure_sig, hir::Safety::Safe),
187+
self.cx(),
188+
self.cx().signature_unclosure(closure_sig, hir::Safety::Safe),
174189
)
175190
}
176-
_ => ty.super_fold_with(self),
177-
}
191+
ty::Adt(def, _) => {
192+
let generics = self.cx().generics_of(def.did());
193+
if generics
194+
.own_params
195+
.iter()
196+
.any(|param| param.default_value(self.cx()).is_some())
197+
{
198+
// We have a type that has default types, like the allocator in Vec. We decided
199+
// to show `Vec` itself, because it hasn't yet been replaced by an `_` `Infer`,
200+
// but we want to ensure that the type parameter with default types does *not*
201+
// get replaced with `_` because then we'd end up with `Vec<_, _>`, instead of
202+
// `Vec<_>`.
203+
self.do_not_hide_nested_type = true;
204+
ty.super_fold_with(self)
205+
} else if ty.has_infer() || self.do_not_hide_nested_type {
206+
// This type has an unsubstituted type variable, meaning that this type has a
207+
// (potentially deeply nested) type parameter from the corresponding type's
208+
// definition. We have explicitly asked this type to not be hidden. In either
209+
// case, we keep the type and don't substitute with `_` just yet.
210+
ty.super_fold_with(self)
211+
} else {
212+
// When we have a type that doesn't have any inference variables, so we replace
213+
// the whole thing with `_`. The type system already knows about this type in
214+
// its entirety and it is redundant to specify it for the user. The user only
215+
// needs to specify the type parameters that we *couldn't* figure out.
216+
self.new_infer()
217+
}
218+
}
219+
_ if ty.has_infer() || self.do_not_hide_nested_type => {
220+
// This type has a (potentially nested) type parameter that we couldn't figure out.
221+
// We will print this depth of type, so at least the type name and at least one of
222+
// its type parameters. We unset `do_not_hide_nested_type` because this type can't
223+
// have type parameter defaults until next type we hit an ADT.
224+
self.do_not_hide_nested_type = false;
225+
ty.super_fold_with(self)
226+
}
227+
// We don't have an unknown type parameter anywhere, replace with `_`.
228+
_ => self.new_infer(),
229+
};
230+
self.do_not_hide_nested_type = prev;
231+
ty
232+
}
233+
234+
fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> {
235+
let prev = self.do_not_hide_nested_type;
236+
// Avoid accidentally erasing the type of the const.
237+
self.do_not_hide_nested_type = true;
238+
let c = c.super_fold_with(self);
239+
self.do_not_hide_nested_type = prev;
240+
c
178241
}
179242
}
180243

181244
fn fmt_printer<'a, 'tcx>(infcx: &'a InferCtxt<'tcx>, ns: Namespace) -> FmtPrinter<'a, 'tcx> {
182245
let mut printer = FmtPrinter::new(infcx.tcx, ns);
183246
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-
188247
let var_origin = infcx.type_var_origin(ty_vid);
189248
if let Some(def_id) = var_origin.param_def_id
190249
// The `Self` param of a trait has the def-id of the trait,
@@ -219,9 +278,9 @@ fn ty_to_string<'tcx>(
219278
) -> String {
220279
let mut printer = fmt_printer(infcx, Namespace::TypeNS);
221280
let ty = infcx.resolve_vars_if_possible(ty);
222-
// We use `fn` ptr syntax for closures, but this only works when the closure
223-
// does not capture anything.
224-
let ty = ty.fold_with(&mut ClosureEraser { tcx: infcx.tcx });
281+
// We use `fn` ptr syntax for closures, but this only works when the closure does not capture
282+
// anything. We also remove all type parameters that are fully known to the type system.
283+
let ty = ty.fold_with(&mut ClosureEraser { infcx, do_not_hide_nested_type: false });
225284

226285
match (ty.kind(), called_method_def_id) {
227286
// 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)