Skip to content

Commit 5ebacd1

Browse files
authored
Rollup merge of #135965 - estebank:shorten-ty-sugg, r=lcnr
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); | ++++++++++++++ ``` Fix #135919.
2 parents 9e89fee + 576db13 commit 5ebacd1

8 files changed

+113
-22
lines changed

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

+77-10
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ 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::inherent::*;
22+
use rustc_type_ir::visit::TypeVisitableExt;
2123
use tracing::{debug, instrument, warn};
2224

2325
use super::nice_region_error::placeholder_error::Highlighted;
@@ -155,27 +157,92 @@ impl UnderspecifiedArgKind {
155157
}
156158
}
157159

158-
struct ClosureEraser<'tcx> {
159-
tcx: TyCtxt<'tcx>,
160+
struct ClosureEraser<'a, 'tcx> {
161+
infcx: &'a InferCtxt<'tcx>,
160162
}
161163

162-
impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ClosureEraser<'tcx> {
164+
impl<'a, 'tcx> ClosureEraser<'a, 'tcx> {
165+
fn new_infer(&mut self) -> Ty<'tcx> {
166+
self.infcx.next_ty_var(DUMMY_SP)
167+
}
168+
}
169+
170+
impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for ClosureEraser<'a, 'tcx> {
163171
fn cx(&self) -> TyCtxt<'tcx> {
164-
self.tcx
172+
self.infcx.tcx
165173
}
166174

167175
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
168176
match ty.kind() {
169177
ty::Closure(_, args) => {
178+
// For a closure type, we turn it into a function pointer so that it gets rendered
179+
// as `fn(args) -> Ret`.
170180
let closure_sig = args.as_closure().sig();
171181
Ty::new_fn_ptr(
172-
self.tcx,
173-
self.tcx.signature_unclosure(closure_sig, hir::Safety::Safe),
182+
self.cx(),
183+
self.cx().signature_unclosure(closure_sig, hir::Safety::Safe),
174184
)
175185
}
176-
_ => ty.super_fold_with(self),
186+
ty::Adt(_, args) if !args.iter().any(|a| a.has_infer()) => {
187+
// We have a type that doesn't have any inference variables, so we replace
188+
// the whole thing with `_`. The type system already knows about this type in
189+
// its entirety and it is redundant to specify it for the user. The user only
190+
// needs to specify the type parameters that we *couldn't* figure out.
191+
self.new_infer()
192+
}
193+
ty::Adt(def, args) => {
194+
let generics = self.cx().generics_of(def.did());
195+
let generics: Vec<bool> = generics
196+
.own_params
197+
.iter()
198+
.map(|param| param.default_value(self.cx()).is_some())
199+
.collect();
200+
let ty = Ty::new_adt(
201+
self.cx(),
202+
*def,
203+
self.cx().mk_args_from_iter(generics.into_iter().zip(args.iter()).map(
204+
|(has_default, arg)| {
205+
if arg.has_infer() {
206+
// This param has an unsubstituted type variable, meaning that this
207+
// type has a (potentially deeply nested) type parameter from the
208+
// corresponding type's definition. We have explicitly asked this
209+
// type to not be hidden. In either case, we keep the type and don't
210+
// substitute with `_` just yet.
211+
arg.fold_with(self)
212+
} else if has_default {
213+
// We have a type param that has a default type, like the allocator
214+
// in Vec. We decided to show `Vec` itself, because it hasn't yet
215+
// been replaced by an `_` `Infer`, but we want to ensure that the
216+
// type parameter with default types does *not* get replaced with
217+
// `_` because then we'd end up with `Vec<_, _>`, instead of
218+
// `Vec<_>`.
219+
arg
220+
} else if let GenericArgKind::Type(_) = arg.kind() {
221+
// We don't replace lifetime or const params, only type params.
222+
self.new_infer().into()
223+
} else {
224+
arg.fold_with(self)
225+
}
226+
},
227+
)),
228+
);
229+
ty
230+
}
231+
_ if ty.has_infer() => {
232+
// This type has a (potentially nested) type parameter that we couldn't figure out.
233+
// We will print this depth of type, so at least the type name and at least one of
234+
// its type parameters.
235+
ty.super_fold_with(self)
236+
}
237+
// We don't have an unknown type parameter anywhere, replace with `_`.
238+
_ => self.new_infer(),
177239
}
178240
}
241+
242+
fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> {
243+
// Avoid accidentally erasing the type of the const.
244+
c
245+
}
179246
}
180247

181248
fn fmt_printer<'a, 'tcx>(infcx: &'a InferCtxt<'tcx>, ns: Namespace) -> FmtPrinter<'a, 'tcx> {
@@ -219,9 +286,9 @@ fn ty_to_string<'tcx>(
219286
) -> String {
220287
let mut printer = fmt_printer(infcx, Namespace::TypeNS);
221288
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 });
289+
// We use `fn` ptr syntax for closures, but this only works when the closure does not capture
290+
// anything. We also remove all type parameters that are fully known to the type system.
291+
let ty = ty.fold_with(&mut ClosureEraser { infcx });
225292

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