Skip to content
Merged
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
107 changes: 76 additions & 31 deletions compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
Original file line number Diff line number Diff line change
@@ -315,8 +315,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
body_id: Option<hir::BodyId>,
failure_span: Span,
arg: GenericArg<'tcx>,
// FIXME(#94483): Either use this or remove it.
_impl_candidates: Vec<ty::TraitRef<'tcx>>,
error_code: TypeAnnotationNeeded,
should_label_span: bool,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
@@ -534,6 +532,23 @@ enum InferSourceKind<'tcx> {
},
}

impl<'tcx> InferSource<'tcx> {
fn from_expansion(&self) -> bool {
let source_from_expansion = match self.kind {
InferSourceKind::LetBinding { insert_span, .. }
| InferSourceKind::ClosureArg { insert_span, .. }
| InferSourceKind::GenericArg { insert_span, .. } => insert_span.from_expansion(),
InferSourceKind::FullyQualifiedMethodCall { receiver, .. } => {
receiver.span.from_expansion()
}
InferSourceKind::ClosureReturn { data, should_wrap_expr, .. } => {
data.span().from_expansion() || should_wrap_expr.map_or(false, Span::from_expansion)
}
};
source_from_expansion || self.span.from_expansion()
}
}

impl<'tcx> InferSourceKind<'tcx> {
fn ty_msg(&self, infcx: &InferCtxt<'_, 'tcx>) -> String {
match *self {
@@ -604,57 +619,85 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> {
/// Sources with a small cost are prefer and should result
/// in a clearer and idiomatic suggestion.
fn source_cost(&self, source: &InferSource<'tcx>) -> usize {
let tcx = self.infcx.tcx;

fn arg_cost<'tcx>(arg: GenericArg<'tcx>) -> usize {
match arg.unpack() {
GenericArgKind::Lifetime(_) => 0, // erased
GenericArgKind::Type(ty) => ty_cost(ty),
GenericArgKind::Const(_) => 3, // some non-zero value
}
#[derive(Clone, Copy)]
struct CostCtxt<'tcx> {
tcx: TyCtxt<'tcx>,
}
fn ty_cost<'tcx>(ty: Ty<'tcx>) -> usize {
match ty.kind() {
ty::Closure(..) => 100,
ty::FnDef(..) => 20,
ty::FnPtr(..) => 10,
ty::Infer(..) => 0,
_ => 1,
impl<'tcx> CostCtxt<'tcx> {
fn arg_cost(self, arg: GenericArg<'tcx>) -> usize {
match arg.unpack() {
GenericArgKind::Lifetime(_) => 0, // erased
GenericArgKind::Type(ty) => self.ty_cost(ty),
GenericArgKind::Const(_) => 3, // some non-zero value
}
}
fn ty_cost(self, ty: Ty<'tcx>) -> usize {
match *ty.kind() {
ty::Closure(..) => 1000,
ty::FnDef(..) => 150,
ty::FnPtr(..) => 30,
ty::Adt(def, substs) => {
5 + self
.tcx
.generics_of(def.did())
.own_substs_no_defaults(self.tcx, substs)
.iter()
.map(|&arg| self.arg_cost(arg))
.sum::<usize>()
}
ty::Tuple(args) => 5 + args.iter().map(|arg| self.ty_cost(arg)).sum::<usize>(),
ty::Ref(_, ty, _) => 2 + self.ty_cost(ty),
ty::Infer(..) => 0,
_ => 1,
}
}
}

// The sources are listed in order of preference here.
match source.kind {
InferSourceKind::LetBinding { ty, .. } => ty_cost(ty),
InferSourceKind::ClosureArg { ty, .. } => 5 + ty_cost(ty),
let tcx = self.infcx.tcx;
let ctx = CostCtxt { tcx };
let base_cost = match source.kind {
InferSourceKind::LetBinding { ty, .. } => ctx.ty_cost(ty),
InferSourceKind::ClosureArg { ty, .. } => ctx.ty_cost(ty),
InferSourceKind::GenericArg { def_id, generic_args, .. } => {
let variant_cost = match tcx.def_kind(def_id) {
DefKind::Variant | DefKind::Ctor(CtorOf::Variant, _) => 15, // `None::<u32>` and friends are ugly.
_ => 12,
// `None::<u32>` and friends are ugly.
DefKind::Variant | DefKind::Ctor(CtorOf::Variant, _) => 15,
_ => 10,
};
variant_cost + generic_args.iter().map(|&arg| arg_cost(arg)).sum::<usize>()
variant_cost + generic_args.iter().map(|&arg| ctx.arg_cost(arg)).sum::<usize>()
}
InferSourceKind::FullyQualifiedMethodCall { substs, .. } => {
20 + substs.iter().map(|arg| arg_cost(arg)).sum::<usize>()
20 + substs.iter().map(|arg| ctx.arg_cost(arg)).sum::<usize>()
}
InferSourceKind::ClosureReturn { ty, should_wrap_expr, .. } => {
30 + ty_cost(ty) + if should_wrap_expr.is_some() { 10 } else { 0 }
30 + ctx.ty_cost(ty) + if should_wrap_expr.is_some() { 10 } else { 0 }
}
}
};

let suggestion_may_apply = if source.from_expansion() { 10000 } else { 0 };

base_cost + suggestion_may_apply
}

/// Uses `fn source_cost` to determine whether this inference source is preferable to
/// previous sources. We generally prefer earlier sources.
#[instrument(level = "debug", skip(self))]
fn update_infer_source(&mut self, new_source: InferSource<'tcx>) {
let cost = self.source_cost(&new_source) + self.attempt;
debug!(?cost);
self.attempt += 1;
if cost < self.infer_source_cost {
self.infer_source_cost = cost;
self.infer_source = Some(new_source);
}
}

fn node_substs_opt(&self, hir_id: HirId) -> Option<SubstsRef<'tcx>> {
let substs = self.typeck_results.node_substs_opt(hir_id);
self.infcx.resolve_vars_if_possible(substs)
}

fn opt_node_type(&self, hir_id: HirId) -> Option<Ty<'tcx>> {
let ty = self.typeck_results.node_type_opt(hir_id);
self.infcx.resolve_vars_if_possible(ty)
@@ -737,7 +780,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> {
let tcx = self.infcx.tcx;
match expr.kind {
hir::ExprKind::Path(ref path) => {
if let Some(substs) = self.typeck_results.node_substs_opt(expr.hir_id) {
if let Some(substs) = self.node_substs_opt(expr.hir_id) {
return self.path_inferred_subst_iter(expr.hir_id, substs, path);
}
}
@@ -765,7 +808,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> {
if generics.has_impl_trait() {
None?
}
let substs = self.typeck_results.node_substs_opt(expr.hir_id)?;
let substs = self.node_substs_opt(expr.hir_id)?;
let span = tcx.hir().span(segment.hir_id?);
let insert_span = segment.ident.span.shrink_to_hi().with_hi(span.hi());
InsertableGenericArgs {
@@ -980,8 +1023,10 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> {
debug!(?args);
let InsertableGenericArgs { insert_span, substs, generics_def_id, def_id } = args;
let generics = tcx.generics_of(generics_def_id);
if let Some(argument_index) =
generics.own_substs(substs).iter().position(|&arg| self.generic_arg_is_target(arg))
if let Some(argument_index) = generics
.own_substs(substs)
.iter()
.position(|&arg| self.generic_arg_contains_target(arg))
{
let substs = self.infcx.resolve_vars_if_possible(substs);
let generic_args = &generics.own_substs_no_defaults(tcx, substs)
@@ -1037,7 +1082,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> {
.any(|generics| generics.has_impl_trait())
};
if let ExprKind::MethodCall(path, args, span) = expr.kind
&& let Some(substs) = self.typeck_results.node_substs_opt(expr.hir_id)
&& let Some(substs) = self.node_substs_opt(expr.hir_id)
&& substs.iter().any(|arg| self.generic_arg_contains_target(arg))
&& let Some(def_id) = self.typeck_results.type_dependent_def_id(expr.hir_id)
&& self.infcx.tcx.trait_of_item(def_id).is_some()
27 changes: 3 additions & 24 deletions compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
Original file line number Diff line number Diff line change
@@ -1980,7 +1980,6 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
body_id,
span,
trait_ref.self_ty().skip_binder().into(),
vec![],
ErrorCode::E0282,
false,
)
@@ -2005,19 +2004,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
let subst = data.trait_ref.substs.iter().find(|s| s.has_infer_types_or_consts());

let mut err = if let Some(subst) = subst {
let impl_candidates = self
.find_similar_impl_candidates(trait_ref)
.into_iter()
.map(|candidate| candidate.trait_ref)
.collect();
self.emit_inference_failure_err(
body_id,
span,
subst,
impl_candidates,
ErrorCode::E0283,
true,
)
self.emit_inference_failure_err(body_id, span, subst, ErrorCode::E0283, true)
} else {
struct_span_err!(
self.tcx.sess,
@@ -2117,7 +2104,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
return;
}

self.emit_inference_failure_err(body_id, span, arg, vec![], ErrorCode::E0282, false)
self.emit_inference_failure_err(body_id, span, arg, ErrorCode::E0282, false)
}

ty::PredicateKind::Subtype(data) => {
@@ -2131,14 +2118,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
let SubtypePredicate { a_is_expected: _, a, b } = data;
// both must be type variables, or the other would've been instantiated
assert!(a.is_ty_var() && b.is_ty_var());
self.emit_inference_failure_err(
body_id,
span,
a.into(),
vec![],
ErrorCode::E0282,
true,
)
self.emit_inference_failure_err(body_id, span, a.into(), ErrorCode::E0282, true)
}
ty::PredicateKind::Projection(data) => {
if predicate.references_error() || self.is_tainted_by_errors() {
@@ -2155,7 +2135,6 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
body_id,
span,
subst,
vec![],
ErrorCode::E0284,
true,
);
11 changes: 2 additions & 9 deletions compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
Original file line number Diff line number Diff line change
@@ -1538,15 +1538,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ty
} else {
if !self.is_tainted_by_errors() {
self.emit_inference_failure_err(
(**self).body_id,
sp,
ty.into(),
vec![],
E0282,
true,
)
.emit();
self.emit_inference_failure_err((**self).body_id, sp, ty.into(), E0282, true)
.emit();
}
let err = self.tcx.ty_error();
self.demand_suptype(sp, err, ty);
2 changes: 0 additions & 2 deletions compiler/rustc_typeck/src/check/writeback.rs
Original file line number Diff line number Diff line change
@@ -692,7 +692,6 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> {
Some(self.body.id()),
self.span.to_span(self.tcx),
t.into(),
vec![],
E0282,
false,
)
@@ -707,7 +706,6 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> {
Some(self.body.id()),
self.span.to_span(self.tcx),
c.into(),
vec![],
E0282,
false,
)
7 changes: 6 additions & 1 deletion src/test/ui/inference/ambiguous_type_parameter.stderr
Original file line number Diff line number Diff line change
@@ -2,7 +2,12 @@ error[E0282]: type annotations needed
--> $DIR/ambiguous_type_parameter.rs:16:19
|
LL | InMemoryStore.get_raw(&String::default());
| ^^^^^^^ cannot infer type for type parameter `K`
| ^^^^^^^
|
help: try using a fully qualified path to specify the expected types
|
LL | <InMemoryStore as Store<String, HashMap<K, String>>>::get_raw(&InMemoryStore, &String::default());
| +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ~

error: aborting due to previous error

2 changes: 1 addition & 1 deletion src/test/ui/inference/cannot-infer-partial-try-return.rs
Original file line number Diff line number Diff line change
@@ -16,8 +16,8 @@ fn infallible() -> Result<(), std::convert::Infallible> {

fn main() {
let x = || -> Result<_, QualifiedError<_>> {
//~^ ERROR type annotations needed for `Result<(), QualifiedError<_>>`
infallible()?;
Ok(())
//~^ ERROR type annotations needed
};
}
15 changes: 7 additions & 8 deletions src/test/ui/inference/cannot-infer-partial-try-return.stderr
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
error[E0282]: type annotations needed for `Result<(), QualifiedError<_>>`
--> $DIR/cannot-infer-partial-try-return.rs:18:13
error[E0282]: type annotations needed
--> $DIR/cannot-infer-partial-try-return.rs:20:9
|
LL | let x = || -> Result<_, QualifiedError<_>> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL |
LL | infallible()?;
| ------------- type must be known at this point
LL | Ok(())
| ^^ cannot infer type of the type parameter `E` declared on the enum `Result`
|
help: try giving this closure an explicit return type
help: consider specifying the generic arguments
|
LL | let x = || -> Result<(), QualifiedError<_>> {
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
LL | Ok::<(), QualifiedError<_>>(())
| +++++++++++++++++++++++++

error: aborting due to previous error

19 changes: 19 additions & 0 deletions src/test/ui/inference/need_type_info/channel.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Test that we suggest specifying the generic argument of `channel`
// instead of the return type of that function, which is a lot more
// complex.
use std::sync::mpsc::channel;

fn no_tuple() {
let _data =
channel(); //~ ERROR type annotations needed
}

fn tuple() {
let (_sender, _receiver) =
channel(); //~ ERROR type annotations needed
}

fn main() {
no_tuple();
tuple();
}
25 changes: 25 additions & 0 deletions src/test/ui/inference/need_type_info/channel.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
error[E0282]: type annotations needed
--> $DIR/channel.rs:8:9
|
LL | channel();
| ^^^^^^^ cannot infer type of the type parameter `T` declared on the function `channel`
|
help: consider specifying the generic argument
|
LL | channel::<T>();
| +++++

error[E0282]: type annotations needed
--> $DIR/channel.rs:13:9
|
LL | channel();
| ^^^^^^^ cannot infer type of the type parameter `T` declared on the function `channel`
|
help: consider specifying the generic argument
|
LL | channel::<T>();
| +++++

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0282`.
9 changes: 7 additions & 2 deletions src/test/ui/issues/issue-23041.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
error[E0282]: type annotations needed
--> $DIR/issue-23041.rs:6:22
--> $DIR/issue-23041.rs:6:7
|
LL | b.downcast_ref::<fn(_)->_>();
| ^^^^^^^^ cannot infer type
| ^^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the associated function `downcast_ref`
|
help: consider specifying the generic arguments
|
LL | b.downcast_ref::<fn(_) -> _>();
| ~~~~~~~~~~~~~~

error: aborting due to previous error

9 changes: 7 additions & 2 deletions src/test/ui/issues/issue-24013.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
error[E0282]: type annotations needed
--> $DIR/issue-24013.rs:5:20
--> $DIR/issue-24013.rs:5:13
|
LL | unsafe {swap::<&mut _>(transmute(&a), transmute(&b))};
| ^^^^^^ cannot infer type
| ^^^^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `swap`
|
help: consider specifying the generic arguments
|
LL | unsafe {swap::<&mut _>(transmute(&a), transmute(&b))};
| ~~~~~~~~~~

error: aborting due to previous error

4 changes: 2 additions & 2 deletions src/test/ui/issues/issue-25368.rs
Original file line number Diff line number Diff line change
@@ -5,10 +5,10 @@ use std::marker::PhantomData;
struct Foo<T> {foo: PhantomData<T>}

fn main() {
let (tx, rx) = //~ ERROR type annotations needed
let (tx, rx) =
channel();
// FIXME(#89862): Suggest adding a generic argument to `channel` instead
spawn(move || {
tx.send(Foo{ foo: PhantomData });
//~^ ERROR type annotations needed
});
}
14 changes: 7 additions & 7 deletions src/test/ui/issues/issue-25368.stderr
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
error[E0282]: type annotations needed for `(Sender<Foo<T>>, std::sync::mpsc::Receiver<Foo<T>>)`
--> $DIR/issue-25368.rs:8:9
error[E0282]: type annotations needed
--> $DIR/issue-25368.rs:11:27
|
LL | let (tx, rx) =
| ^^^^^^^^
LL | tx.send(Foo{ foo: PhantomData });
| ^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the struct `PhantomData`
|
help: consider giving this pattern a type, where the type for type parameter `T` is specified
help: consider specifying the generic argument
|
LL | let (tx, rx): (Sender<Foo<T>>, std::sync::mpsc::Receiver<Foo<T>>) =
| +++++++++++++++++++++++++++++++++++++++++++++++++++++
LL | tx.send(Foo{ foo: PhantomData::<T> });
| +++++

error: aborting due to previous error

Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@ error[E0283]: type annotations needed
--> $DIR/method-ambig-one-trait-unknown-int-type.rs:26:7
|
LL | x.foo();
| ^^^ cannot infer type for struct `Vec<_>`
| ^^^
|
note: multiple `impl`s satisfying `Vec<_>: Foo` found
--> $DIR/method-ambig-one-trait-unknown-int-type.rs:9:1
@@ -23,6 +23,10 @@ LL | impl Foo for Vec<usize> {
...
LL | impl Foo for Vec<isize> {
| ^^^^^^^^^^^^^^^^^^^^^^^
help: try using a fully qualified path to specify the expected types
|
LL | <Vec<T> as Foo>::foo(&x);
| ++++++++++++++++++++++ ~
Comment on lines +26 to +29
Copy link
Contributor

Choose a reason for hiding this comment

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

This should have been <Vec<isize> as Foo>::foo(&x), but that'd be outside of scope for this PR.


error[E0308]: mismatched types
--> $DIR/method-ambig-one-trait-unknown-int-type.rs:33:20