diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs index 99b70c87ccd1c..9e7e96dddd7c1 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs @@ -18,6 +18,8 @@ use rustc_middle::ty::{ TypeFoldable, TypeFolder, TypeSuperFoldable, TypeckResults, }; use rustc_span::{BytePos, DUMMY_SP, FileName, Ident, Span, sym}; +use rustc_type_ir::inherent::*; +use rustc_type_ir::visit::TypeVisitableExt; use tracing::{debug, instrument, warn}; use super::nice_region_error::placeholder_error::Highlighted; @@ -155,27 +157,92 @@ impl UnderspecifiedArgKind { } } -struct ClosureEraser<'tcx> { - tcx: TyCtxt<'tcx>, +struct ClosureEraser<'a, 'tcx> { + infcx: &'a InferCtxt<'tcx>, } -impl<'tcx> TypeFolder> for ClosureEraser<'tcx> { +impl<'a, 'tcx> ClosureEraser<'a, 'tcx> { + fn new_infer(&mut self) -> Ty<'tcx> { + self.infcx.next_ty_var(DUMMY_SP) + } +} + +impl<'a, 'tcx> TypeFolder> for ClosureEraser<'a, 'tcx> { fn cx(&self) -> TyCtxt<'tcx> { - self.tcx + self.infcx.tcx } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { match ty.kind() { ty::Closure(_, args) => { + // For a closure type, we turn it into a function pointer so that it gets rendered + // as `fn(args) -> Ret`. let closure_sig = args.as_closure().sig(); Ty::new_fn_ptr( - self.tcx, - self.tcx.signature_unclosure(closure_sig, hir::Safety::Safe), + self.cx(), + self.cx().signature_unclosure(closure_sig, hir::Safety::Safe), ) } - _ => ty.super_fold_with(self), + ty::Adt(_, args) if !args.iter().any(|a| a.has_infer()) => { + // We have a type that doesn't have any inference variables, so we replace + // the whole thing with `_`. The type system already knows about this type in + // its entirety and it is redundant to specify it for the user. The user only + // needs to specify the type parameters that we *couldn't* figure out. + self.new_infer() + } + ty::Adt(def, args) => { + let generics = self.cx().generics_of(def.did()); + let generics: Vec = generics + .own_params + .iter() + .map(|param| param.default_value(self.cx()).is_some()) + .collect(); + let ty = Ty::new_adt( + self.cx(), + *def, + self.cx().mk_args_from_iter(generics.into_iter().zip(args.iter()).map( + |(has_default, arg)| { + if arg.has_infer() { + // This param has an unsubstituted type variable, meaning that this + // type has a (potentially deeply nested) type parameter from the + // corresponding type's definition. We have explicitly asked this + // type to not be hidden. In either case, we keep the type and don't + // substitute with `_` just yet. + arg.fold_with(self) + } else if has_default { + // We have a type param that has a default type, like the allocator + // in Vec. We decided to show `Vec` itself, because it hasn't yet + // been replaced by an `_` `Infer`, but we want to ensure that the + // type parameter with default types does *not* get replaced with + // `_` because then we'd end up with `Vec<_, _>`, instead of + // `Vec<_>`. + arg + } else if let GenericArgKind::Type(_) = arg.kind() { + // We don't replace lifetime or const params, only type params. + self.new_infer().into() + } else { + arg.fold_with(self) + } + }, + )), + ); + ty + } + _ if ty.has_infer() => { + // This type has a (potentially nested) type parameter that we couldn't figure out. + // We will print this depth of type, so at least the type name and at least one of + // its type parameters. + ty.super_fold_with(self) + } + // We don't have an unknown type parameter anywhere, replace with `_`. + _ => self.new_infer(), } } + + fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> { + // Avoid accidentally erasing the type of the const. + c + } } fn fmt_printer<'a, 'tcx>(infcx: &'a InferCtxt<'tcx>, ns: Namespace) -> FmtPrinter<'a, 'tcx> { @@ -219,9 +286,9 @@ fn ty_to_string<'tcx>( ) -> String { let mut printer = fmt_printer(infcx, Namespace::TypeNS); let ty = infcx.resolve_vars_if_possible(ty); - // We use `fn` ptr syntax for closures, but this only works when the closure - // does not capture anything. - let ty = ty.fold_with(&mut ClosureEraser { tcx: infcx.tcx }); + // We use `fn` ptr syntax for closures, but this only works when the closure does not capture + // anything. We also remove all type parameters that are fully known to the type system. + let ty = ty.fold_with(&mut ClosureEraser { infcx }); match (ty.kind(), called_method_def_id) { // We don't want the regular output for `fn`s because it includes its path in diff --git a/tests/ui/inference/cannot-infer-closure-circular.stderr b/tests/ui/inference/cannot-infer-closure-circular.stderr index a16e832f8ef91..ee17f7737cf30 100644 --- a/tests/ui/inference/cannot-infer-closure-circular.stderr +++ b/tests/ui/inference/cannot-infer-closure-circular.stderr @@ -9,8 +9,8 @@ LL | Ok(v) | help: consider giving this closure parameter an explicit type, where the type for type parameter `E` is specified | -LL | let x = |r: Result<(), E>| { - | +++++++++++++++ +LL | let x = |r: Result<_, E>| { + | ++++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/inference/erase-type-params-in-label.stderr b/tests/ui/inference/erase-type-params-in-label.stderr index 4e9a74c1e403d..1ec8a33eb8205 100644 --- a/tests/ui/inference/erase-type-params-in-label.stderr +++ b/tests/ui/inference/erase-type-params-in-label.stderr @@ -12,8 +12,8 @@ LL | fn foo(t: T, k: K) -> Foo { | ^^^^^^^ required by this bound in `foo` help: consider giving `foo` an explicit type, where the type for type parameter `W` is specified | -LL | let foo: Foo = foo(1, ""); - | ++++++++++++++++++++++ +LL | let foo: Foo<_, &_, W, Z> = foo(1, ""); + | ++++++++++++++++++ error[E0283]: type annotations needed for `Bar` --> $DIR/erase-type-params-in-label.rs:5:9 @@ -29,8 +29,8 @@ LL | fn bar(t: T, k: K) -> Bar { | ^^^^^^^ required by this bound in `bar` help: consider giving `bar` an explicit type, where the type for type parameter `Z` is specified | -LL | let bar: Bar = bar(1, ""); - | +++++++++++++++++++ +LL | let bar: Bar<_, &_, Z> = bar(1, ""); + | +++++++++++++++ error: aborting due to 2 previous errors diff --git a/tests/ui/inference/issue-104649.stderr b/tests/ui/inference/issue-104649.stderr index 391ed16f349d7..27382e301341f 100644 --- a/tests/ui/inference/issue-104649.stderr +++ b/tests/ui/inference/issue-104649.stderr @@ -6,8 +6,8 @@ LL | let a = A(Result::Ok(Result::Ok(()))); | help: consider giving `a` an explicit type, where the type for type parameter `E` is specified | -LL | let a: A, Error>> = A(Result::Ok(Result::Ok(()))); - | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +LL | let a: A, _>> = A(Result::Ok(Result::Ok(()))); + | ++++++++++++++++++++++++++++++++++++++++++++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/inference/issue-83606.stderr b/tests/ui/inference/issue-83606.stderr index 69d1d71ef3cc0..97ccad9e785e3 100644 --- a/tests/ui/inference/issue-83606.stderr +++ b/tests/ui/inference/issue-83606.stderr @@ -11,8 +11,8 @@ LL | fn foo(_: impl std::fmt::Display) -> [usize; N] { | ^^^^^^^^^^^^^^ required by this const generic parameter in `foo` help: consider giving this pattern a type, where the value of const parameter `N` is specified | -LL | let _: [usize; N] = foo("foo"); - | ++++++++++++ +LL | let _: [_; N] = foo("foo"); + | ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/inference/really-long-type-in-let-binding-without-sufficient-type-info.rs b/tests/ui/inference/really-long-type-in-let-binding-without-sufficient-type-info.rs new file mode 100644 index 0000000000000..4fd15eea9e0f1 --- /dev/null +++ b/tests/ui/inference/really-long-type-in-let-binding-without-sufficient-type-info.rs @@ -0,0 +1,10 @@ +type A = (i32, i32, i32, i32); +type B = (A, A, A, A); +type C = (B, B, B, B); +type D = (C, C, C, C); + +fn foo(x: D) { + let y = Err(x); //~ ERROR type annotations needed for `Result<_ +} + +fn main() {} diff --git a/tests/ui/inference/really-long-type-in-let-binding-without-sufficient-type-info.stderr b/tests/ui/inference/really-long-type-in-let-binding-without-sufficient-type-info.stderr new file mode 100644 index 0000000000000..65fe2ffcb7f16 --- /dev/null +++ b/tests/ui/inference/really-long-type-in-let-binding-without-sufficient-type-info.stderr @@ -0,0 +1,14 @@ +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))))>` + --> $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 = Err(x); + | ++++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/type-inference/or_else-multiple-type-params.stderr b/tests/ui/type-inference/or_else-multiple-type-params.stderr index 3176a2d490ebc..9bcd07f8bf164 100644 --- a/tests/ui/type-inference/or_else-multiple-type-params.stderr +++ b/tests/ui/type-inference/or_else-multiple-type-params.stderr @@ -6,8 +6,8 @@ LL | .or_else(|err| { | help: try giving this closure an explicit return type | -LL | .or_else(|err| -> Result { - | +++++++++++++++++++ +LL | .or_else(|err| -> Result<_, F> { + | +++++++++++++++ error: aborting due to 1 previous error