Skip to content

Commit d2d49d2

Browse files
authored
Rollup merge of rust-lang#63507 - estebank:type-inference-error, r=Centril
When needing type annotations in local bindings, account for impl Trait and closures Fix rust-lang#46680, fix rust-lang#63504, fix rust-lang#63506, fix rust-lang#40014, cc rust-lang#63502.
2 parents aaeff01 + 6c3a98e commit d2d49d2

25 files changed

+405
-74
lines changed

src/librustc/hir/map/mod.rs

+27-5
Original file line numberDiff line numberDiff line change
@@ -649,12 +649,34 @@ impl<'hir> Map<'hir> {
649649
}
650650
}
651651

652-
pub fn is_const_scope(&self, hir_id: HirId) -> bool {
653-
self.walk_parent_nodes(hir_id, |node| match *node {
654-
Node::Item(Item { node: ItemKind::Const(_, _), .. }) => true,
655-
Node::Item(Item { node: ItemKind::Fn(_, header, _, _), .. }) => header.is_const(),
652+
/// Whether the expression pointed at by `hir_id` belongs to a `const` evaluation context.
653+
/// Used exclusively for diagnostics, to avoid suggestion function calls.
654+
pub fn is_const_context(&self, hir_id: HirId) -> bool {
655+
let parent_id = self.get_parent_item(hir_id);
656+
match self.get(parent_id) {
657+
Node::Item(&Item {
658+
node: ItemKind::Const(..),
659+
..
660+
})
661+
| Node::TraitItem(&TraitItem {
662+
node: TraitItemKind::Const(..),
663+
..
664+
})
665+
| Node::ImplItem(&ImplItem {
666+
node: ImplItemKind::Const(..),
667+
..
668+
})
669+
| Node::AnonConst(_)
670+
| Node::Item(&Item {
671+
node: ItemKind::Static(..),
672+
..
673+
}) => true,
674+
Node::Item(&Item {
675+
node: ItemKind::Fn(_, header, ..),
676+
..
677+
}) => header.constness == Constness::Const,
656678
_ => false,
657-
}, |_| false).map(|id| id != CRATE_HIR_ID).unwrap_or(false)
679+
}
658680
}
659681

660682
/// If there is some error when walking the parents (e.g., a node does not

src/librustc/hir/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1541,7 +1541,7 @@ pub enum ExprKind {
15411541
Match(P<Expr>, HirVec<Arm>, MatchSource),
15421542
/// A closure (e.g., `move |a, b, c| {a + b + c}`).
15431543
///
1544-
/// The final span is the span of the argument block `|...|`.
1544+
/// The `Span` is the argument block `|...|`.
15451545
///
15461546
/// This may also be a generator literal or an `async block` as indicated by the
15471547
/// `Option<GeneratorMovability>`.

src/librustc/infer/error_reporting/need_type_info.rs

+161-51
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
use crate::hir::def::Namespace;
2-
use crate::hir::{self, Local, Pat, Body, HirId};
2+
use crate::hir::{self, Body, FunctionRetTy, Expr, ExprKind, HirId, Local, Pat};
33
use crate::hir::intravisit::{self, Visitor, NestedVisitorMap};
44
use crate::infer::InferCtxt;
55
use crate::infer::type_variable::TypeVariableOriginKind;
66
use crate::ty::{self, Ty, Infer, TyVar};
77
use crate::ty::print::Print;
88
use syntax::source_map::DesugaringKind;
99
use syntax_pos::Span;
10-
use errors::DiagnosticBuilder;
10+
use errors::{Applicability, DiagnosticBuilder};
1111

1212
struct FindLocalByTypeVisitor<'a, 'tcx> {
1313
infcx: &'a InferCtxt<'a, 'tcx>,
@@ -16,9 +16,26 @@ struct FindLocalByTypeVisitor<'a, 'tcx> {
1616
found_local_pattern: Option<&'tcx Pat>,
1717
found_arg_pattern: Option<&'tcx Pat>,
1818
found_ty: Option<Ty<'tcx>>,
19+
found_closure: Option<&'tcx ExprKind>,
1920
}
2021

2122
impl<'a, 'tcx> FindLocalByTypeVisitor<'a, 'tcx> {
23+
fn new(
24+
infcx: &'a InferCtxt<'a, 'tcx>,
25+
target_ty: Ty<'tcx>,
26+
hir_map: &'a hir::map::Map<'tcx>,
27+
) -> Self {
28+
Self {
29+
infcx,
30+
target_ty,
31+
hir_map,
32+
found_local_pattern: None,
33+
found_arg_pattern: None,
34+
found_ty: None,
35+
found_closure: None,
36+
}
37+
}
38+
2239
fn node_matches_type(&mut self, hir_id: HirId) -> Option<Ty<'tcx>> {
2340
let ty_opt = self.infcx.in_progress_tables.and_then(|tables| {
2441
tables.borrow().node_type_opt(hir_id)
@@ -72,6 +89,60 @@ impl<'a, 'tcx> Visitor<'tcx> for FindLocalByTypeVisitor<'a, 'tcx> {
7289
}
7390
intravisit::walk_body(self, body);
7491
}
92+
93+
fn visit_expr(&mut self, expr: &'tcx Expr) {
94+
if let (ExprKind::Closure(_, _fn_decl, _id, _sp, _), Some(_)) = (
95+
&expr.node,
96+
self.node_matches_type(expr.hir_id),
97+
) {
98+
self.found_closure = Some(&expr.node);
99+
}
100+
intravisit::walk_expr(self, expr);
101+
}
102+
}
103+
104+
/// Suggest giving an appropriate return type to a closure expression.
105+
fn closure_return_type_suggestion(
106+
span: Span,
107+
err: &mut DiagnosticBuilder<'_>,
108+
output: &FunctionRetTy,
109+
body: &Body,
110+
name: &str,
111+
ret: &str,
112+
) {
113+
let (arrow, post) = match output {
114+
FunctionRetTy::DefaultReturn(_) => ("-> ", " "),
115+
_ => ("", ""),
116+
};
117+
let suggestion = match body.value.node {
118+
ExprKind::Block(..) => {
119+
vec![(output.span(), format!("{}{}{}", arrow, ret, post))]
120+
}
121+
_ => {
122+
vec![
123+
(output.span(), format!("{}{}{}{{ ", arrow, ret, post)),
124+
(body.value.span.shrink_to_hi(), " }".to_string()),
125+
]
126+
}
127+
};
128+
err.multipart_suggestion(
129+
"give this closure an explicit return type without `_` placeholders",
130+
suggestion,
131+
Applicability::HasPlaceholders,
132+
);
133+
err.span_label(span, InferCtxt::missing_type_msg(&name));
134+
}
135+
136+
/// Given a closure signature, return a `String` containing a list of all its argument types.
137+
fn closure_args(fn_sig: &ty::PolyFnSig<'_>) -> String {
138+
fn_sig.inputs()
139+
.skip_binder()
140+
.iter()
141+
.next()
142+
.map(|args| args.tuple_fields()
143+
.map(|arg| arg.to_string())
144+
.collect::<Vec<_>>().join(", "))
145+
.unwrap_or_default()
75146
}
76147

77148
impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
@@ -106,16 +177,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
106177
let ty = self.resolve_vars_if_possible(&ty);
107178
let name = self.extract_type_name(&ty, None);
108179

109-
let mut err_span = span;
110-
111-
let mut local_visitor = FindLocalByTypeVisitor {
112-
infcx: &self,
113-
target_ty: ty,
114-
hir_map: &self.tcx.hir(),
115-
found_local_pattern: None,
116-
found_arg_pattern: None,
117-
found_ty: None,
118-
};
180+
let mut local_visitor = FindLocalByTypeVisitor::new(&self, ty, &self.tcx.hir());
119181
let ty_to_string = |ty: Ty<'tcx>| -> String {
120182
let mut s = String::new();
121183
let mut printer = ty::print::FmtPrinter::new(self.tcx, &mut s, Namespace::TypeNS);
@@ -136,6 +198,31 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
136198
let expr = self.tcx.hir().expect_expr(body_id.hir_id);
137199
local_visitor.visit_expr(expr);
138200
}
201+
let err_span = if let Some(pattern) = local_visitor.found_arg_pattern {
202+
pattern.span
203+
} else {
204+
span
205+
};
206+
207+
let is_named_and_not_impl_trait = |ty: Ty<'_>| {
208+
&ty.to_string() != "_" &&
209+
// FIXME: Remove this check after `impl_trait_in_bindings` is stabilized. #63527
210+
(!ty.is_impl_trait() || self.tcx.features().impl_trait_in_bindings)
211+
};
212+
213+
let ty_msg = match local_visitor.found_ty {
214+
Some(ty::TyS { sty: ty::Closure(def_id, substs), .. }) => {
215+
let fn_sig = substs.closure_sig(*def_id, self.tcx);
216+
let args = closure_args(&fn_sig);
217+
let ret = fn_sig.output().skip_binder().to_string();
218+
format!(" for the closure `fn({}) -> {}`", args, ret)
219+
}
220+
Some(ty) if is_named_and_not_impl_trait(ty) => {
221+
let ty = ty_to_string(ty);
222+
format!(" for `{}`", ty)
223+
}
224+
_ => String::new(),
225+
};
139226

140227
// When `name` corresponds to a type argument, show the path of the full type we're
141228
// trying to infer. In the following example, `ty_msg` contains
@@ -150,27 +237,58 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
150237
// | consider giving `b` the explicit type `std::result::Result<i32, E>`, where
151238
// | the type parameter `E` is specified
152239
// ```
153-
let (ty_msg, suffix) = match &local_visitor.found_ty {
154-
Some(ty) if &ty.to_string() != "_" && name == "_" => {
240+
let mut err = struct_span_err!(
241+
self.tcx.sess,
242+
err_span,
243+
E0282,
244+
"type annotations needed{}",
245+
ty_msg,
246+
);
247+
248+
let suffix = match local_visitor.found_ty {
249+
Some(ty::TyS { sty: ty::Closure(def_id, substs), .. }) => {
250+
let fn_sig = substs.closure_sig(*def_id, self.tcx);
251+
let ret = fn_sig.output().skip_binder().to_string();
252+
253+
if let Some(ExprKind::Closure(_, decl, body_id, ..)) = local_visitor.found_closure {
254+
if let Some(body) = self.tcx.hir().krate().bodies.get(body_id) {
255+
closure_return_type_suggestion(
256+
span,
257+
&mut err,
258+
&decl.output,
259+
&body,
260+
&name,
261+
&ret,
262+
);
263+
// We don't want to give the other suggestions when the problem is the
264+
// closure return type.
265+
return err;
266+
}
267+
}
268+
269+
// This shouldn't be reachable, but just in case we leave a reasonable fallback.
270+
let args = closure_args(&fn_sig);
271+
// This suggestion is incomplete, as the user will get further type inference
272+
// errors due to the `_` placeholders and the introduction of `Box`, but it does
273+
// nudge them in the right direction.
274+
format!("a boxed closure type like `Box<dyn Fn({}) -> {}>`", args, ret)
275+
}
276+
Some(ty) if is_named_and_not_impl_trait(ty) && name == "_" => {
155277
let ty = ty_to_string(ty);
156-
(format!(" for `{}`", ty),
157-
format!("the explicit type `{}`, with the type parameters specified", ty))
278+
format!("the explicit type `{}`, with the type parameters specified", ty)
158279
}
159-
Some(ty) if &ty.to_string() != "_" && ty.to_string() != name => {
280+
Some(ty) if is_named_and_not_impl_trait(ty) && ty.to_string() != name => {
160281
let ty = ty_to_string(ty);
161-
(format!(" for `{}`", ty),
162-
format!(
163-
"the explicit type `{}`, where the type parameter `{}` is specified",
282+
format!(
283+
"the explicit type `{}`, where the type parameter `{}` is specified",
164284
ty,
165285
name,
166-
))
286+
)
167287
}
168-
_ => (String::new(), "a type".to_owned()),
288+
_ => "a type".to_string(),
169289
};
170-
let mut labels = vec![(span, InferCtxt::missing_type_msg(&name))];
171290

172291
if let Some(pattern) = local_visitor.found_arg_pattern {
173-
err_span = pattern.span;
174292
// We don't want to show the default label for closures.
175293
//
176294
// So, before clearing, the output would look something like this:
@@ -187,39 +305,31 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
187305
// ^ consider giving this closure parameter the type `[_; 0]`
188306
// with the type parameter `_` specified
189307
// ```
190-
labels.clear();
191-
labels.push((
308+
err.span_label(
192309
pattern.span,
193310
format!("consider giving this closure parameter {}", suffix),
194-
));
311+
);
195312
} else if let Some(pattern) = local_visitor.found_local_pattern {
196-
if let Some(simple_ident) = pattern.simple_ident() {
313+
let msg = if let Some(simple_ident) = pattern.simple_ident() {
197314
match pattern.span.desugaring_kind() {
198-
None => labels.push((
199-
pattern.span,
200-
format!("consider giving `{}` {}", simple_ident, suffix),
201-
)),
202-
Some(DesugaringKind::ForLoop) => labels.push((
203-
pattern.span,
204-
"the element type for this iterator is not specified".to_owned(),
205-
)),
206-
_ => {}
315+
None => {
316+
format!("consider giving `{}` {}", simple_ident, suffix)
317+
}
318+
Some(DesugaringKind::ForLoop) => {
319+
"the element type for this iterator is not specified".to_string()
320+
}
321+
_ => format!("this needs {}", suffix),
207322
}
208323
} else {
209-
labels.push((pattern.span, format!("consider giving this pattern {}", suffix)));
210-
}
211-
};
212-
213-
let mut err = struct_span_err!(
214-
self.tcx.sess,
215-
err_span,
216-
E0282,
217-
"type annotations needed{}",
218-
ty_msg,
219-
);
220-
221-
for (target_span, label_message) in labels {
222-
err.span_label(target_span, label_message);
324+
format!("consider giving this pattern {}", suffix)
325+
};
326+
err.span_label(pattern.span, msg);
327+
}
328+
if !err.span.span_labels().iter().any(|span_label| {
329+
span_label.label.is_some() && span_label.span == span
330+
}) && local_visitor.found_arg_pattern.is_none()
331+
{ // Avoid multiple labels pointing at `span`.
332+
err.span_label(span, InferCtxt::missing_type_msg(&name));
223333
}
224334

225335
err

src/librustc/ty/sty.rs

+3
Original file line numberDiff line numberDiff line change
@@ -2068,6 +2068,9 @@ impl<'tcx> TyS<'tcx> {
20682068
Error => { // ignore errors (#54954)
20692069
ty::Binder::dummy(FnSig::fake())
20702070
}
2071+
Closure(..) => bug!(
2072+
"to get the signature of a closure, use `closure_sig()` not `fn_sig()`",
2073+
),
20712074
_ => bug!("Ty::fn_sig() called on non-fn type: {:?}", self)
20722075
}
20732076
}

src/librustc_typeck/check/demand.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
127127

128128
self.suggest_compatible_variants(&mut err, expr, expected, expr_ty);
129129
self.suggest_ref_or_into(&mut err, expr, expected, expr_ty);
130+
self.suggest_boxing_when_appropriate(&mut err, expr, expected, expr_ty);
130131
self.suggest_missing_await(&mut err, expr, expected, expr_ty);
131132

132133
(expected, Some(err))
@@ -548,7 +549,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
548549
checked_ty: Ty<'tcx>,
549550
expected_ty: Ty<'tcx>,
550551
) -> bool {
551-
if self.tcx.hir().is_const_scope(expr.hir_id) {
552+
if self.tcx.hir().is_const_context(expr.hir_id) {
552553
// Shouldn't suggest `.into()` on `const`s.
553554
// FIXME(estebank): modify once we decide to suggest `as` casts
554555
return false;

0 commit comments

Comments
 (0)