Skip to content

Commit

Permalink
Rollup merge of #98761 - lcnr:need_type_info-cont, r=estebank
Browse files Browse the repository at this point in the history
more `need_type_info` improvements

this now deals with macros in suggestions and the source cost computation does what I want for `channel` 🎉

r? ``@estebank``
  • Loading branch information
Dylan-DPC committed Jul 5, 2022
2 parents d26ccf7 + f475e88 commit 6a9db39
Show file tree
Hide file tree
Showing 14 changed files with 167 additions and 90 deletions.
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
Expand Up @@ -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> {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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);
}
}
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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()
Expand Down
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
Expand Up @@ -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,
)
Expand All @@ -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,
Expand Down Expand Up @@ -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) => {
Expand All @@ -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() {
Expand All @@ -2155,7 +2135,6 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
body_id,
span,
subst,
vec![],
ErrorCode::E0284,
true,
);
Expand Down
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
Expand Up @@ -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);
Expand Down
2 changes: 0 additions & 2 deletions compiler/rustc_typeck/src/check/writeback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)
Expand All @@ -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,
)
Expand Down
7 changes: 6 additions & 1 deletion src/test/ui/inference/ambiguous_type_parameter.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
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
Expand Up @@ -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

Expand Down
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

Expand Down
Loading

0 comments on commit 6a9db39

Please sign in to comment.