mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-27 18:57:42 +03:00
When single impl can satisfy inference error, suggest type
When encountering an inference error where a return type must be known, like when calling `Iterator::sum::<T>()` without specifying `T`, look for all `T` that would satisfy `Sum<S>`. If only one, suggest it. ``` error[E0283]: type annotations needed --> $DIR/cannot-infer-iterator-sum-return-type.rs:4:9 | LL | let sum = v | ^^^ ... LL | .sum(); // `sum::<T>` needs `T` to be specified | --- type must be known at this point | = note: cannot satisfy `_: Sum<i32>` help: the trait `Sum` is implemented for `i32` --> $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL ::: $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL | = note: in this macro invocation note: required by a bound in `std::iter::Iterator::sum` --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL = note: this error originates in the macro `integer_sum_product` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider giving `sum` an explicit type, where the type for type parameter `S` is specified | LL | let sum: i32 = v | +++++ ```
This commit is contained in:
@@ -158,6 +158,7 @@ fn try_get_prefix(&self) -> Option<&str> {
|
||||
|
||||
struct ClosureEraser<'a, 'tcx> {
|
||||
infcx: &'a InferCtxt<'tcx>,
|
||||
depth: usize,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> ClosureEraser<'a, 'tcx> {
|
||||
@@ -172,7 +173,8 @@ fn cx(&self) -> TyCtxt<'tcx> {
|
||||
}
|
||||
|
||||
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||
match ty.kind() {
|
||||
self.depth += 1;
|
||||
let ty = 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`.
|
||||
@@ -233,9 +235,15 @@ fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||
// its type parameters.
|
||||
ty.super_fold_with(self)
|
||||
}
|
||||
// We don't have an unknown type parameter anywhere, replace with `_`.
|
||||
// We are in the top-level type, not one of its type parameters. Name it with its
|
||||
// parameters replaced.
|
||||
_ if self.depth == 1 => ty.super_fold_with(self),
|
||||
// We don't have an unknown type parameter anywhere, and we are in a type parameter.
|
||||
// Replace with `_`.
|
||||
_ => self.new_infer(),
|
||||
}
|
||||
};
|
||||
self.depth -= 1;
|
||||
ty
|
||||
}
|
||||
|
||||
fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> {
|
||||
@@ -287,7 +295,7 @@ fn ty_to_string<'tcx>(
|
||||
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. We also remove all type parameters that are fully known to the type system.
|
||||
let ty = ty.fold_with(&mut ClosureEraser { infcx });
|
||||
let ty = ty.fold_with(&mut ClosureEraser { infcx, depth: 0 });
|
||||
|
||||
match (ty.kind(), called_method_def_id) {
|
||||
// We don't want the regular output for `fn`s because it includes its path in
|
||||
@@ -467,6 +475,25 @@ pub fn emit_inference_failure_err(
|
||||
term: Term<'tcx>,
|
||||
error_code: TypeAnnotationNeeded,
|
||||
should_label_span: bool,
|
||||
) -> Diag<'a> {
|
||||
self.emit_inference_failure_err_with_type_hint(
|
||||
body_def_id,
|
||||
failure_span,
|
||||
term,
|
||||
error_code,
|
||||
should_label_span,
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn emit_inference_failure_err_with_type_hint(
|
||||
&self,
|
||||
body_def_id: LocalDefId,
|
||||
failure_span: Span,
|
||||
term: Term<'tcx>,
|
||||
error_code: TypeAnnotationNeeded,
|
||||
should_label_span: bool,
|
||||
ty: Option<Ty<'tcx>>,
|
||||
) -> Diag<'a> {
|
||||
let term = self.resolve_vars_if_possible(term);
|
||||
let arg_data = self
|
||||
@@ -479,7 +506,7 @@ pub fn emit_inference_failure_err(
|
||||
return self.bad_inference_failure_err(failure_span, arg_data, error_code);
|
||||
};
|
||||
|
||||
let mut local_visitor = FindInferSourceVisitor::new(self, typeck_results, term);
|
||||
let mut local_visitor = FindInferSourceVisitor::new(self, typeck_results, term, ty);
|
||||
if let Some(body) = self.tcx.hir_maybe_body_owned_by(
|
||||
self.tcx.typeck_root_def_id(body_def_id.to_def_id()).expect_local(),
|
||||
) {
|
||||
@@ -779,10 +806,20 @@ fn ty_localized_msg(&self, infcx: &InferCtxt<'tcx>) -> (&'static str, String, Op
|
||||
| InferSourceKind::ClosureReturn { ty, .. } => {
|
||||
if ty.is_closure() {
|
||||
("closure", closure_as_fn_str(infcx, ty), long_ty_path)
|
||||
} else if !ty.is_ty_or_numeric_infer() {
|
||||
("normal", infcx.tcx.short_string(ty, &mut long_ty_path), long_ty_path)
|
||||
} else {
|
||||
} else if ty.is_ty_or_numeric_infer()
|
||||
|| ty.is_primitive()
|
||||
|| matches!(
|
||||
ty.kind(),
|
||||
ty::Adt(_, args)
|
||||
if args.types().count() == 0 && args.consts().count() == 0
|
||||
)
|
||||
{
|
||||
// `ty` is either `_`, a primitive type like `u32` or a type with no type or
|
||||
// const parameters. We will not mention the type in the main inference error
|
||||
// message.
|
||||
("other", String::new(), long_ty_path)
|
||||
} else {
|
||||
("normal", infcx.tcx.short_string(ty, &mut long_ty_path), long_ty_path)
|
||||
}
|
||||
}
|
||||
// FIXME: We should be able to add some additional info here.
|
||||
@@ -815,6 +852,7 @@ struct FindInferSourceVisitor<'a, 'tcx> {
|
||||
typeck_results: &'a TypeckResults<'tcx>,
|
||||
|
||||
target: Term<'tcx>,
|
||||
ty: Option<Ty<'tcx>>,
|
||||
|
||||
attempt: usize,
|
||||
infer_source_cost: usize,
|
||||
@@ -826,12 +864,14 @@ fn new(
|
||||
tecx: &'a TypeErrCtxt<'a, 'tcx>,
|
||||
typeck_results: &'a TypeckResults<'tcx>,
|
||||
target: Term<'tcx>,
|
||||
ty: Option<Ty<'tcx>>,
|
||||
) -> Self {
|
||||
FindInferSourceVisitor {
|
||||
tecx,
|
||||
typeck_results,
|
||||
|
||||
target,
|
||||
ty,
|
||||
|
||||
attempt: 0,
|
||||
infer_source_cost: usize::MAX,
|
||||
@@ -1213,7 +1253,7 @@ fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
|
||||
fn visit_local(&mut self, local: &'tcx LetStmt<'tcx>) {
|
||||
intravisit::walk_local(self, local);
|
||||
|
||||
if let Some(ty) = self.opt_node_type(local.hir_id) {
|
||||
if let Some(mut ty) = self.opt_node_type(local.hir_id) {
|
||||
if self.generic_arg_contains_target(ty.into()) {
|
||||
fn get_did(
|
||||
typeck_results: &TypeckResults<'_>,
|
||||
@@ -1241,7 +1281,11 @@ fn get_did(
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(t) = self.ty
|
||||
&& ty.has_infer()
|
||||
{
|
||||
ty = t;
|
||||
}
|
||||
if let LocalSource::Normal = local.source
|
||||
&& local.ty.is_none()
|
||||
{
|
||||
|
||||
@@ -246,12 +246,37 @@ pub(super) fn maybe_report_ambiguity(
|
||||
.find(|s| s.has_non_region_infer());
|
||||
|
||||
let mut err = if let Some(term) = term {
|
||||
self.emit_inference_failure_err(
|
||||
let candidates: Vec<_> = self
|
||||
.tcx
|
||||
.all_impls(trait_pred.def_id())
|
||||
.filter_map(|def_id| {
|
||||
let imp = self.tcx.impl_trait_header(def_id);
|
||||
if imp.polarity != ty::ImplPolarity::Positive
|
||||
|| !self.tcx.is_user_visible_dep(def_id.krate)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
let imp = imp.trait_ref.skip_binder();
|
||||
if imp
|
||||
.with_replaced_self_ty(self.tcx, trait_pred.skip_binder().self_ty())
|
||||
== trait_pred.skip_binder().trait_ref
|
||||
{
|
||||
Some(imp.self_ty())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
self.emit_inference_failure_err_with_type_hint(
|
||||
obligation.cause.body_id,
|
||||
span,
|
||||
term,
|
||||
TypeAnnotationNeeded::E0283,
|
||||
true,
|
||||
match &candidates[..] {
|
||||
[candidate] => Some(*candidate),
|
||||
_ => None,
|
||||
},
|
||||
)
|
||||
} else {
|
||||
struct_span_code_err!(
|
||||
|
||||
@@ -2251,6 +2251,16 @@ pub(super) fn report_similar_impl_candidates(
|
||||
if candidates.is_empty() {
|
||||
return false;
|
||||
}
|
||||
let mut specific_candidates = candidates.clone();
|
||||
specific_candidates.retain(|(tr, _)| {
|
||||
tr.with_replaced_self_ty(self.tcx, trait_pred.skip_binder().self_ty())
|
||||
== trait_pred.skip_binder().trait_ref
|
||||
});
|
||||
if !specific_candidates.is_empty() {
|
||||
// We have found a subset of impls that fully satisfy the expected trait, only
|
||||
// mention those types.
|
||||
candidates = specific_candidates;
|
||||
}
|
||||
if let &[(cand, def_id)] = &candidates[..] {
|
||||
if self.tcx.is_diagnostic_item(sym::FromResidual, cand.def_id)
|
||||
&& !self.tcx.features().enabled(sym::try_trait_v2)
|
||||
|
||||
@@ -17,10 +17,10 @@ note: required by a bound in `foo`
|
||||
|
|
||||
LL | fn foo<T: Foo>(_: [u8; T::N]) -> T {
|
||||
| ^^^ required by this bound in `foo`
|
||||
help: consider giving this pattern a type
|
||||
help: consider giving this pattern a type, where the type for type parameter `T` is specified
|
||||
|
|
||||
LL | let _: /* Type */ = foo([0; 1]);
|
||||
| ++++++++++++
|
||||
LL | let _: u8 = foo([0; 1]);
|
||||
| ++++
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
//@ run-rustfix
|
||||
fn main() {
|
||||
let v = vec![1, 2];
|
||||
let sum: i32 = v //~ ERROR: type annotations needed
|
||||
.iter()
|
||||
.map(|val| *val)
|
||||
.sum(); // `sum::<T>` needs `T` to be specified
|
||||
// In this case any integer would fit, but we resolve to `i32` because that's what `{integer}`
|
||||
// got coerced to. If the user needs further hinting that they can change the integer type, that
|
||||
// can come from other suggestions. (#100802)
|
||||
let bool = sum > 0;
|
||||
assert_eq!(bool, true);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
//@ run-rustfix
|
||||
fn main() {
|
||||
let v = vec![1, 2];
|
||||
let sum = v //~ ERROR: type annotations needed
|
||||
.iter()
|
||||
.map(|val| *val)
|
||||
.sum(); // `sum::<T>` needs `T` to be specified
|
||||
// In this case any integer would fit, but we resolve to `i32` because that's what `{integer}`
|
||||
// got coerced to. If the user needs further hinting that they can change the integer type, that
|
||||
// can come from other suggestions. (#100802)
|
||||
let bool = sum > 0;
|
||||
assert_eq!(bool, true);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
error[E0283]: type annotations needed
|
||||
--> $DIR/cannot-infer-iterator-sum-return-type.rs:4:9
|
||||
|
|
||||
LL | let sum = v
|
||||
| ^^^
|
||||
...
|
||||
LL | .sum(); // `sum::<T>` needs `T` to be specified
|
||||
| --- type must be known at this point
|
||||
|
|
||||
= note: the type must implement `Sum<i32>`
|
||||
help: the trait `Sum` is implemented for `i32`
|
||||
--> $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL
|
||||
::: $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL
|
||||
|
|
||||
= note: in this macro invocation
|
||||
note: required by a bound in `std::iter::Iterator::sum`
|
||||
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
|
||||
= note: this error originates in the macro `integer_sum_product` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
help: consider giving `sum` an explicit type, where the type for type parameter `S` is specified
|
||||
|
|
||||
LL | let sum: i32 = v
|
||||
| +++++
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0283`.
|
||||
Reference in New Issue
Block a user