Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Only shown relevant type params in E0283 label #90709

Merged
merged 4 commits into from
Dec 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 131 additions & 6 deletions compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::infer::type_variable::TypeVariableOriginKind;
use crate::infer::InferCtxt;
use crate::rustc_middle::ty::TypeFoldable;
use crate::infer::{InferCtxt, Symbol};
use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder};
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Namespace};
Expand All @@ -11,7 +10,7 @@ use rustc_middle::hir::map::Map;
use rustc_middle::infer::unify_key::ConstVariableOriginKind;
use rustc_middle::ty::print::Print;
use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
use rustc_middle::ty::{self, DefIdTree, InferConst, Ty, TyCtxt};
use rustc_middle::ty::{self, Const, DefIdTree, InferConst, Ty, TyCtxt, TypeFoldable, TypeFolder};
use rustc_span::symbol::kw;
use rustc_span::Span;
use std::borrow::Cow;
Expand Down Expand Up @@ -306,6 +305,15 @@ pub enum UnderspecifiedArgKind {
Const { is_parameter: bool },
}

impl UnderspecifiedArgKind {
fn descr(&self) -> &'static str {
match self {
Self::Type { .. } => "type",
Self::Const { .. } => "const",
}
}
}

impl InferenceDiagnosticsData {
/// Generate a label for a generic argument which can't be inferred. When not
/// much is known about the argument, `use_diag` may be used to describe the
Expand Down Expand Up @@ -588,6 +596,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
}
}

let param_type = arg_data.kind.descr();
let suffix = match local_visitor.found_node_ty {
Some(ty) if ty.is_closure() => {
let substs =
Expand Down Expand Up @@ -626,13 +635,15 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
}
Some(ty) if is_named_and_not_impl_trait(ty) && arg_data.name == "_" => {
let ty = ty_to_string(ty);
format!("the explicit type `{}`, with the type parameters specified", ty)
format!("the explicit type `{}`, with the {} parameters specified", ty, param_type)
}
Some(ty) if is_named_and_not_impl_trait(ty) && ty.to_string() != arg_data.name => {
let ty = ResolvedTypeParamEraser::new(self.tcx).fold_ty(ty);
let ty = ErrTypeParamEraser(self.tcx).fold_ty(ty);
let ty = ty_to_string(ty);
format!(
"the explicit type `{}`, where the type parameter `{}` is specified",
ty, arg_data.name,
"the explicit type `{}`, where the {} parameter `{}` is specified",
ty, param_type, arg_data.name,
)
}
_ => "a type".to_string(),
Expand Down Expand Up @@ -908,3 +919,117 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
err
}
}

/// Turn *resolved* type params into `[type error]` to signal we don't want to display them. After
/// performing that replacement, we'll turn all remaining infer type params to use their name from
/// their definition, and replace all the `[type error]`s back to being infer so they display in
/// the output as `_`. If we didn't go through `[type error]`, we would either show all type params
/// by their name *or* `_`, neither of which is desireable: we want to show all types that we could
/// infer as `_` to reduce verbosity and avoid telling the user about unnecessary type annotations.
struct ResolvedTypeParamEraser<'tcx> {
tcx: TyCtxt<'tcx>,
level: usize,
}

impl<'tcx> ResolvedTypeParamEraser<'tcx> {
fn new(tcx: TyCtxt<'tcx>) -> Self {
ResolvedTypeParamEraser { tcx, level: 0 }
}

/// Replace not yet inferred const params with their def name.
fn replace_infers(&self, c: &'tcx Const<'tcx>, index: u32, name: Symbol) -> &'tcx Const<'tcx> {
match c.val {
ty::ConstKind::Infer(..) => self.tcx().mk_const_param(index, name, c.ty),
_ => c,
}
}
}

impl<'tcx> TypeFolder<'tcx> for ResolvedTypeParamEraser<'tcx> {
fn tcx<'a>(&'a self) -> TyCtxt<'tcx> {
self.tcx
}
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
self.level += 1;
let t = match t.kind() {
// We'll hide this type only if all its type params are hidden as well.
ty::Adt(def, substs) => {
let generics = self.tcx().generics_of(def.did);
// Account for params with default values, like `Vec`, where we
// want to show `Vec<T>`, not `Vec<T, _>`. If we replaced that
// subst, then we'd get the incorrect output, so we passthrough.
let substs: Vec<_> = substs
.iter()
.zip(generics.params.iter())
.map(|(subst, param)| match &(subst.unpack(), &param.kind) {
(_, ty::GenericParamDefKind::Type { has_default: true, .. }) => subst,
(crate::infer::GenericArgKind::Const(c), _) => {
self.replace_infers(c, param.index, param.name).into()
}
_ => subst.super_fold_with(self),
})
.collect();
let should_keep = |subst: &GenericArg<'_>| match subst.unpack() {
ty::subst::GenericArgKind::Type(t) => match t.kind() {
ty::Error(_) => false,
_ => true,
},
// Account for `const` params here, otherwise `doesnt_infer.rs`
// shows `_` instead of `Foo<{ _: u32 }>`
ty::subst::GenericArgKind::Const(_) => true,
_ => false,
};
if self.level == 1 || substs.iter().any(should_keep) {
let substs = self.tcx().intern_substs(&substs[..]);
self.tcx().mk_ty(ty::Adt(def, substs))
} else {
self.tcx().ty_error()
}
}
ty::Ref(_, ty, _) => {
let ty = self.fold_ty(ty);
match ty.kind() {
// Avoid `&_`, these can be safely presented as `_`.
ty::Error(_) => self.tcx().ty_error(),
_ => t.super_fold_with(self),
}
}
// We could account for `()` if we wanted to replace it, but it's assured to be short.
ty::Tuple(_)
| ty::Slice(_)
| ty::RawPtr(_)
| ty::FnDef(..)
| ty::FnPtr(_)
| ty::Opaque(..)
| ty::Projection(_)
| ty::Never => t.super_fold_with(self),
ty::Array(ty, c) => self
.tcx()
.mk_ty(ty::Array(self.fold_ty(ty), self.replace_infers(c, 0, Symbol::intern("N")))),
// We don't want to hide type params that haven't been resolved yet.
// This would be the type that will be written out with the type param
// name in the output.
ty::Infer(_) => t,
// We don't want to hide the outermost type, only its type params.
_ if self.level == 1 => t.super_fold_with(self),
// Hide this type
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm slightly worried about the handling of ty::Errors that come in as part of the input as they become indistinguishable from those that are produced by this TypeFolder, but its probably fine…

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would think that any naturally ocurring ty::Error should be displayed as _ anyways. Worst case scenario on the next wave of errors the user will be told to fill it in.

_ => self.tcx().ty_error(),
};
self.level -= 1;
t
}
}

/// Replace `[type error]` with `ty::Infer(ty::Var)` to display `_`.
struct ErrTypeParamEraser<'tcx>(TyCtxt<'tcx>);
impl<'tcx> TypeFolder<'tcx> for ErrTypeParamEraser<'tcx> {
fn tcx<'a>(&'a self) -> TyCtxt<'tcx> {
self.0
}
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
match t.kind() {
ty::Error(_) => self.tcx().mk_ty_var(ty::TyVid::from_u32(0)),
_ => t.super_fold_with(self),
}
}
}
2 changes: 1 addition & 1 deletion src/test/ui/const-generics/defaults/doesnt_infer.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ error[E0282]: type annotations needed for `Foo<{_: u32}>`
LL | let foo = Foo::foo();
| --- ^^^^^^^^ cannot infer the value of const parameter `N`
| |
| consider giving `foo` the explicit type `Foo<{_: u32}>`, where the type parameter `N` is specified
| consider giving `foo` the explicit type `Foo<N>`, where the const parameter `N` is specified

error: aborting due to previous error

Expand Down
27 changes: 27 additions & 0 deletions src/test/ui/inference/erase-type-params-in-label.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
fn main() {
let foo = foo(1, ""); //~ ERROR E0283
}
fn baz() {
let bar = bar(1, ""); //~ ERROR E0283
}

struct Bar<T, K, N: Default> {
t: T,
k: K,
n: N,
}

fn bar<T, K, Z: Default>(t: T, k: K) -> Bar<T, K, Z> {
Bar { t, k, n: Default::default() }
}

struct Foo<T, K, N: Default, M: Default> {
t: T,
k: K,
n: N,
m: M,
}

fn foo<T, K, W: Default, Z: Default>(t: T, k: K) -> Foo<T, K, W, Z> {
Foo { t, k, n: Default::default(), m: Default::default() }
}
41 changes: 41 additions & 0 deletions src/test/ui/inference/erase-type-params-in-label.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
error[E0283]: type annotations needed for `Foo<i32, &str, W, Z>`
--> $DIR/erase-type-params-in-label.rs:2:15
|
LL | let foo = foo(1, "");
| --- ^^^ cannot infer type for type parameter `W` declared on the function `foo`
| |
| consider giving `foo` the explicit type `Foo<_, _, W, Z>`, where the type parameter `W` is specified
|
= note: cannot satisfy `_: Default`
note: required by a bound in `foo`
--> $DIR/erase-type-params-in-label.rs:25:17
|
LL | fn foo<T, K, W: Default, Z: Default>(t: T, k: K) -> Foo<T, K, W, Z> {
| ^^^^^^^ required by this bound in `foo`
help: consider specifying the type arguments in the function call
|
LL | let foo = foo::<T, K, W, Z>(1, "");
| ++++++++++++++
Comment on lines +15 to +18
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Outstanding: changing this to also use _ for the first two params here. Can be done as a follow up.


error[E0283]: type annotations needed for `Bar<i32, &str, Z>`
--> $DIR/erase-type-params-in-label.rs:5:15
|
LL | let bar = bar(1, "");
| --- ^^^ cannot infer type for type parameter `Z` declared on the function `bar`
| |
| consider giving `bar` the explicit type `Bar<_, _, Z>`, where the type parameter `Z` is specified
|
= note: cannot satisfy `_: Default`
note: required by a bound in `bar`
--> $DIR/erase-type-params-in-label.rs:14:17
|
LL | fn bar<T, K, Z: Default>(t: T, k: K) -> Bar<T, K, Z> {
| ^^^^^^^ required by this bound in `bar`
help: consider specifying the type arguments in the function call
|
LL | let bar = bar::<T, K, Z>(1, "");
| +++++++++++

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0283`.
2 changes: 1 addition & 1 deletion src/test/ui/inference/issue-83606.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ error[E0282]: type annotations needed for `[usize; _]`
LL | let _ = foo("foo"); //<- Do not suggest `foo::<N>("foo");`!
| - ^^^ cannot infer the value of const parameter `N` declared on the function `foo`
| |
| consider giving this pattern the explicit type `[usize; _]`, where the type parameter `N` is specified
| consider giving this pattern the explicit type `[_; N]`, where the const parameter `N` is specified

error: aborting due to previous error

Expand Down