Skip to content

Commit cc05561

Browse files
committed
detect wrong number of args when type-checking a closure
Instead of creating inference variables for those argument types, use the trait error-reporting code to give a nicer error.
1 parent bd10ef7 commit cc05561

File tree

5 files changed

+191
-27
lines changed

5 files changed

+191
-27
lines changed

src/librustc/traits/error_reporting.rs

+35-7
Original file line numberDiff line numberDiff line change
@@ -747,7 +747,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
747747
ty::TyTuple(ref tys, _) => tys.iter()
748748
.map(|t| match t.sty {
749749
ty::TypeVariants::TyTuple(ref tys, _) => ArgKind::Tuple(
750-
span,
750+
Some(span),
751751
tys.iter()
752752
.map(|ty| ("_".to_owned(), format!("{}", ty.sty)))
753753
.collect::<Vec<_>>()
@@ -815,7 +815,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
815815
}
816816
}
817817

818-
fn get_fn_like_arguments(&self, node: hir::map::Node) -> (Span, Vec<ArgKind>) {
818+
/// Given some node representing a fn-like thing in the HIR map,
819+
/// returns a span and `ArgKind` information that describes the
820+
/// arguments it expects. This can be supplied to
821+
/// `report_arg_count_mismatch`.
822+
pub fn get_fn_like_arguments(&self, node: hir::map::Node) -> (Span, Vec<ArgKind>) {
819823
match node {
820824
hir::map::NodeExpr(&hir::Expr {
821825
node: hir::ExprClosure(_, ref _decl, id, span, _),
@@ -829,7 +833,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
829833
..
830834
} = arg.pat.clone().into_inner() {
831835
ArgKind::Tuple(
832-
span,
836+
Some(span),
833837
args.iter().map(|pat| {
834838
let snippet = self.tcx.sess.codemap()
835839
.span_to_snippet(pat.span).unwrap();
@@ -862,7 +866,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
862866
(self.tcx.sess.codemap().def_span(span), decl.inputs.iter()
863867
.map(|arg| match arg.clone().into_inner().node {
864868
hir::TyTup(ref tys) => ArgKind::Tuple(
865-
arg.span,
869+
Some(arg.span),
866870
tys.iter()
867871
.map(|_| ("_".to_owned(), "_".to_owned()))
868872
.collect::<Vec<_>>(),
@@ -874,7 +878,10 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
874878
}
875879
}
876880

877-
fn report_arg_count_mismatch(
881+
/// Reports an error when the number of arguments needed by a
882+
/// trait match doesn't match the number that the expression
883+
/// provides.
884+
pub fn report_arg_count_mismatch(
878885
&self,
879886
span: Span,
880887
found_span: Option<Span>,
@@ -1363,13 +1370,34 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
13631370
}
13641371
}
13651372

1366-
enum ArgKind {
1373+
/// Summarizes information
1374+
pub enum ArgKind {
1375+
/// An argument of non-tuple type. Parameters are (name, ty)
13671376
Arg(String, String),
1368-
Tuple(Span, Vec<(String, String)>),
1377+
1378+
/// An argument of tuple type. For a "found" argument, the span is
1379+
/// the locationo in the source of the pattern. For a "expected"
1380+
/// argument, it will be None. The vector is a list of (name, ty)
1381+
/// strings for the components of the tuple.
1382+
Tuple(Option<Span>, Vec<(String, String)>),
13691383
}
13701384

13711385
impl ArgKind {
13721386
fn empty() -> ArgKind {
13731387
ArgKind::Arg("_".to_owned(), "_".to_owned())
13741388
}
1389+
1390+
/// Creates an `ArgKind` from the expected type of an
1391+
/// argument. This has no name (`_`) and no source spans..
1392+
pub fn from_expected_ty(t: Ty<'_>) -> ArgKind {
1393+
match t.sty {
1394+
ty::TyTuple(ref tys, _) => ArgKind::Tuple(
1395+
None,
1396+
tys.iter()
1397+
.map(|ty| ("_".to_owned(), format!("{}", ty.sty)))
1398+
.collect::<Vec<_>>()
1399+
),
1400+
_ => ArgKind::Arg("_".to_owned(), format!("{}", t.sty)),
1401+
}
1402+
}
13751403
}

src/librustc/traits/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ pub use self::util::SupertraitDefIds;
4949
pub use self::util::transitive_bounds;
5050

5151
mod coherence;
52-
mod error_reporting;
52+
pub mod error_reporting;
5353
mod fulfill;
5454
mod project;
5555
mod object_safety;

src/librustc_typeck/check/closure.rs

+112-19
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,24 @@ use rustc::hir::def_id::DefId;
1717
use rustc::infer::{InferOk, InferResult};
1818
use rustc::infer::LateBoundRegionConversionTime;
1919
use rustc::infer::type_variable::TypeVariableOrigin;
20+
use rustc::traits::error_reporting::ArgKind;
2021
use rustc::ty::{self, ToPolyTraitRef, Ty};
2122
use rustc::ty::subst::Substs;
2223
use rustc::ty::TypeFoldable;
2324
use std::cmp;
2425
use std::iter;
2526
use syntax::abi::Abi;
27+
use syntax::codemap::Span;
2628
use rustc::hir;
2729

30+
/// What signature do we *expect* the closure to have from context?
31+
#[derive(Debug)]
32+
struct ExpectedSig<'tcx> {
33+
/// Span that gave us this expectation, if we know that.
34+
cause_span: Option<Span>,
35+
sig: ty::FnSig<'tcx>,
36+
}
37+
2838
struct ClosureSignatures<'tcx> {
2939
bound_sig: ty::PolyFnSig<'tcx>,
3040
liberated_sig: ty::FnSig<'tcx>,
@@ -63,7 +73,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
6373
decl: &'gcx hir::FnDecl,
6474
body: &'gcx hir::Body,
6575
gen: Option<hir::GeneratorMovability>,
66-
expected_sig: Option<ty::FnSig<'tcx>>,
76+
expected_sig: Option<ExpectedSig<'tcx>>,
6777
) -> Ty<'tcx> {
6878
debug!(
6979
"check_closure(opt_kind={:?}, expected_sig={:?})",
@@ -160,10 +170,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
160170
closure_type
161171
}
162172

173+
/// Given the expected type, figures out what it can about this closure we
174+
/// are about to type check:
163175
fn deduce_expectations_from_expected_type(
164176
&self,
165177
expected_ty: Ty<'tcx>,
166-
) -> (Option<ty::FnSig<'tcx>>, Option<ty::ClosureKind>) {
178+
) -> (Option<ExpectedSig<'tcx>>, Option<ty::ClosureKind>) {
167179
debug!(
168180
"deduce_expectations_from_expected_type(expected_ty={:?})",
169181
expected_ty
@@ -175,7 +187,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
175187
.projection_bounds()
176188
.filter_map(|pb| {
177189
let pb = pb.with_self_ty(self.tcx, self.tcx.types.err);
178-
self.deduce_sig_from_projection(&pb)
190+
self.deduce_sig_from_projection(None, &pb)
179191
})
180192
.next();
181193
let kind = object_type
@@ -184,15 +196,21 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
184196
(sig, kind)
185197
}
186198
ty::TyInfer(ty::TyVar(vid)) => self.deduce_expectations_from_obligations(vid),
187-
ty::TyFnPtr(sig) => (Some(sig.skip_binder().clone()), Some(ty::ClosureKind::Fn)),
199+
ty::TyFnPtr(sig) => {
200+
let expected_sig = ExpectedSig {
201+
cause_span: None,
202+
sig: sig.skip_binder().clone(),
203+
};
204+
(Some(expected_sig), Some(ty::ClosureKind::Fn))
205+
}
188206
_ => (None, None),
189207
}
190208
}
191209

192210
fn deduce_expectations_from_obligations(
193211
&self,
194212
expected_vid: ty::TyVid,
195-
) -> (Option<ty::FnSig<'tcx>>, Option<ty::ClosureKind>) {
213+
) -> (Option<ExpectedSig<'tcx>>, Option<ty::ClosureKind>) {
196214
let fulfillment_cx = self.fulfillment_cx.borrow();
197215
// Here `expected_ty` is known to be a type inference variable.
198216

@@ -212,7 +230,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
212230
ty::Predicate::Projection(ref proj_predicate) => {
213231
let trait_ref = proj_predicate.to_poly_trait_ref(self.tcx);
214232
self.self_type_matches_expected_vid(trait_ref, expected_vid)
215-
.and_then(|_| self.deduce_sig_from_projection(proj_predicate))
233+
.and_then(|_| {
234+
self.deduce_sig_from_projection(
235+
Some(obligation.cause.span),
236+
proj_predicate,
237+
)
238+
})
216239
}
217240
_ => None,
218241
}
@@ -262,10 +285,15 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
262285

263286
/// Given a projection like "<F as Fn(X)>::Result == Y", we can deduce
264287
/// everything we need to know about a closure.
288+
///
289+
/// The `cause_span` should be the span that caused us to
290+
/// have this expected signature, or `None` if we can't readily
291+
/// know that.
265292
fn deduce_sig_from_projection(
266293
&self,
294+
cause_span: Option<Span>,
267295
projection: &ty::PolyProjectionPredicate<'tcx>,
268-
) -> Option<ty::FnSig<'tcx>> {
296+
) -> Option<ExpectedSig<'tcx>> {
269297
let tcx = self.tcx;
270298

271299
debug!("deduce_sig_from_projection({:?})", projection);
@@ -297,16 +325,16 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
297325
ret_param_ty
298326
);
299327

300-
let fn_sig = self.tcx.mk_fn_sig(
328+
let sig = self.tcx.mk_fn_sig(
301329
input_tys.cloned(),
302330
ret_param_ty,
303331
false,
304332
hir::Unsafety::Normal,
305333
Abi::Rust,
306334
);
307-
debug!("deduce_sig_from_projection: fn_sig {:?}", fn_sig);
335+
debug!("deduce_sig_from_projection: sig {:?}", sig);
308336

309-
Some(fn_sig)
337+
Some(ExpectedSig { cause_span, sig })
310338
}
311339

312340
fn self_type_matches_expected_vid(
@@ -330,7 +358,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
330358
expr_def_id: DefId,
331359
decl: &hir::FnDecl,
332360
body: &hir::Body,
333-
expected_sig: Option<ty::FnSig<'tcx>>,
361+
expected_sig: Option<ExpectedSig<'tcx>>,
334362
) -> ClosureSignatures<'tcx> {
335363
if let Some(e) = expected_sig {
336364
self.sig_of_closure_with_expectation(expr_def_id, decl, body, e)
@@ -406,7 +434,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
406434
expr_def_id: DefId,
407435
decl: &hir::FnDecl,
408436
body: &hir::Body,
409-
expected_sig: ty::FnSig<'tcx>,
437+
expected_sig: ExpectedSig<'tcx>,
410438
) -> ClosureSignatures<'tcx> {
411439
debug!(
412440
"sig_of_closure_with_expectation(expected_sig={:?})",
@@ -416,20 +444,24 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
416444
// Watch out for some surprises and just ignore the
417445
// expectation if things don't see to match up with what we
418446
// expect.
419-
if expected_sig.variadic != decl.variadic {
420-
return self.sig_of_closure_no_expectation(expr_def_id, decl, body);
421-
} else if expected_sig.inputs_and_output.len() != decl.inputs.len() + 1 {
422-
// we could probably handle this case more gracefully
447+
if expected_sig.sig.variadic != decl.variadic {
423448
return self.sig_of_closure_no_expectation(expr_def_id, decl, body);
449+
} else if expected_sig.sig.inputs_and_output.len() != decl.inputs.len() + 1 {
450+
return self.sig_of_closure_with_mismatched_number_of_arguments(
451+
expr_def_id,
452+
decl,
453+
body,
454+
expected_sig,
455+
);
424456
}
425457

426458
// Create a `PolyFnSig`. Note the oddity that late bound
427459
// regions appearing free in `expected_sig` are now bound up
428460
// in this binder we are creating.
429-
assert!(!expected_sig.has_regions_escaping_depth(1));
461+
assert!(!expected_sig.sig.has_regions_escaping_depth(1));
430462
let bound_sig = ty::Binder(self.tcx.mk_fn_sig(
431-
expected_sig.inputs().iter().cloned(),
432-
expected_sig.output(),
463+
expected_sig.sig.inputs().iter().cloned(),
464+
expected_sig.sig.output(),
433465
decl.variadic,
434466
hir::Unsafety::Normal,
435467
Abi::RustCall,
@@ -455,6 +487,35 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
455487
closure_sigs
456488
}
457489

490+
fn sig_of_closure_with_mismatched_number_of_arguments(
491+
&self,
492+
expr_def_id: DefId,
493+
decl: &hir::FnDecl,
494+
body: &hir::Body,
495+
expected_sig: ExpectedSig<'tcx>,
496+
) -> ClosureSignatures<'tcx> {
497+
let expr_map_node = self.tcx.hir.get_if_local(expr_def_id).unwrap();
498+
let expected_args: Vec<_> = expected_sig
499+
.sig
500+
.inputs()
501+
.iter()
502+
.map(|ty| ArgKind::from_expected_ty(ty))
503+
.collect();
504+
let (closure_span, found_args) = self.get_fn_like_arguments(expr_map_node);
505+
let expected_span = expected_sig.cause_span.unwrap_or(closure_span);
506+
self.report_arg_count_mismatch(
507+
expected_span,
508+
Some(closure_span),
509+
expected_args,
510+
found_args,
511+
true,
512+
).emit();
513+
514+
let error_sig = self.error_sig_of_closure(decl);
515+
516+
self.closure_sigs(expr_def_id, body, error_sig)
517+
}
518+
458519
/// Enforce the user's types against the expectation. See
459520
/// `sig_of_closure_with_expectation` for details on the overall
460521
/// strategy.
@@ -560,6 +621,38 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
560621
result
561622
}
562623

624+
/// Converts the types that the user supplied, in case that doing
625+
/// so should yield an error, but returns back a signature where
626+
/// all parameters are of type `TyErr`.
627+
fn error_sig_of_closure(&self, decl: &hir::FnDecl) -> ty::PolyFnSig<'tcx> {
628+
let astconv: &AstConv = self;
629+
630+
let supplied_arguments = decl.inputs.iter().map(|a| {
631+
// Convert the types that the user supplied (if any), but ignore them.
632+
astconv.ast_ty_to_ty(a);
633+
self.tcx.types.err
634+
});
635+
636+
match decl.output {
637+
hir::Return(ref output) => {
638+
astconv.ast_ty_to_ty(&output);
639+
}
640+
hir::DefaultReturn(_) => {}
641+
}
642+
643+
let result = ty::Binder(self.tcx.mk_fn_sig(
644+
supplied_arguments,
645+
self.tcx.types.err,
646+
decl.variadic,
647+
hir::Unsafety::Normal,
648+
Abi::RustCall,
649+
));
650+
651+
debug!("supplied_sig_of_closure: result={:?}", result);
652+
653+
result
654+
}
655+
563656
fn closure_sigs(
564657
&self,
565658
expr_def_id: DefId,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Regression test for #47244: in this specific scenario, when the
12+
// expected type indicated 1 argument but the closure takes two, we
13+
// would (early on) create type variables for the type of `b`. If the
14+
// user then attempts to invoke a method on `b`, we would get an error
15+
// saying that the type of `b` must be known, which was not very
16+
// helpful.
17+
18+
use std::collections::HashMap;
19+
fn main() {
20+
21+
let m = HashMap::new();
22+
m.insert( "foo", "bar" );
23+
24+
m.iter().map( |_, b| {
25+
//~^ ERROR closure is expected to take a single 2-tuple
26+
27+
b.to_string()
28+
});
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error[E0593]: closure is expected to take a single 2-tuple as argument, but it takes 2 distinct arguments
2+
--> $DIR/closure-arg-count-expected-type-issue-47244.rs:24:14
3+
|
4+
24 | m.iter().map( |_, b| {
5+
| ^^^ ------ takes 2 distinct arguments
6+
| |
7+
| expected closure that takes a single 2-tuple as argument
8+
help: change the closure to accept a tuple instead of individual arguments
9+
|
10+
24 | m.iter().map( |(_, b)| {
11+
| ^^^^^^^^
12+
13+
error: aborting due to previous error
14+

0 commit comments

Comments
 (0)