Skip to content

Commit 50517d5

Browse files
committed
Auto merge of #41488 - estebank:closure-args, r=arielb1
Clean up callable type mismatch errors ```rust error[E0593]: closure takes 1 argument but 2 arguments are required here --> ../../src/test/ui/mismatched_types/closure-arg-count.rs:13:15 | 13 | [1, 2, 3].sort_by(|(tuple, tuple2)| panic!()); | ^^^^^^^ -------------------------- takes 1 argument | | | expected closure that takes 2 arguments ``` instead of ```rust error[E0281]: type mismatch: the type `[closure@../../src/test/ui/mismatched_types/closure-arg-count.rs:13:23: 13:49]` implements the trait `for<'r> std::ops::FnMut<(&'r {integer},)>`, but the trait `for<'r, 'r> std::ops::FnMut<(&'r {integer}, &'r {integer})>` is required (expected a tuple with 2 elements, found one with 1 elements) --> ../../src/test/ui/mismatched_types/closure-arg-count.rs:13:15 | 13 | [1, 2, 3].sort_by(|(tuple, tuple2)| panic!()); | ^^^^^^^ ``` Fix #21857, re #24680.
2 parents 33535af + b10e293 commit 50517d5

16 files changed

+399
-24
lines changed

src/librustc/diagnostics.rs

+21-6
Original file line numberDiff line numberDiff line change
@@ -1049,18 +1049,19 @@ which expected that trait. This error typically occurs when working with
10491049
`Fn`-based types. Erroneous code example:
10501050
10511051
```compile_fail,E0281
1052-
fn foo<F: Fn()>(x: F) { }
1052+
fn foo<F: Fn(usize)>(x: F) { }
10531053
10541054
fn main() {
1055-
// type mismatch: the type ... implements the trait `core::ops::Fn<(_,)>`,
1056-
// but the trait `core::ops::Fn<()>` is required (expected (), found tuple
1055+
// type mismatch: ... implements the trait `core::ops::Fn<(String,)>`,
1056+
// but the trait `core::ops::Fn<(usize,)>` is required
10571057
// [E0281]
1058-
foo(|y| { });
1058+
foo(|y: String| { });
10591059
}
10601060
```
10611061
1062-
The issue in this case is that `foo` is defined as accepting a `Fn` with no
1063-
arguments, but the closure we attempted to pass to it requires one argument.
1062+
The issue in this case is that `foo` is defined as accepting a `Fn` with one
1063+
argument of type `String`, but the closure we attempted to pass to it requires
1064+
one arguments of type `usize`.
10641065
"##,
10651066

10661067
E0282: r##"
@@ -1807,6 +1808,20 @@ makes a difference in practice.)
18071808
[rfc401]: https://github.com/rust-lang/rfcs/blob/master/text/0401-coercions.md
18081809
"##,
18091810

1811+
E0593: r##"
1812+
You tried to supply an `Fn`-based type with an incorrect number of arguments
1813+
than what was expected. Erroneous code example:
1814+
1815+
```compile_fail,E0593
1816+
fn foo<F: Fn()>(x: F) { }
1817+
1818+
fn main() {
1819+
// [E0593] closure takes 1 argument but 0 arguments are required
1820+
foo(|y| { });
1821+
}
1822+
```
1823+
"##,
1824+
18101825
}
18111826

18121827

src/librustc/traits/error_reporting.rs

+103-8
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ use rustc::lint::builtin::EXTRA_REQUIREMENT_IN_IMPL;
3535
use std::fmt;
3636
use syntax::ast::{self, NodeId};
3737
use ty::{self, AdtKind, ToPredicate, ToPolyTraitRef, Ty, TyCtxt, TypeFoldable, TyInfer, TyVar};
38-
use ty::error::ExpectedFound;
38+
use ty::error::{ExpectedFound, TypeError};
3939
use ty::fast_reject;
4040
use ty::fold::TypeFolder;
4141
use ty::subst::Subst;
@@ -663,13 +663,54 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
663663
if actual_trait_ref.self_ty().references_error() {
664664
return;
665665
}
666-
struct_span_err!(self.tcx.sess, span, E0281,
667-
"type mismatch: the type `{}` implements the trait `{}`, \
668-
but the trait `{}` is required ({})",
669-
expected_trait_ref.self_ty(),
670-
expected_trait_ref,
671-
actual_trait_ref,
672-
e)
666+
let expected_trait_ty = expected_trait_ref.self_ty();
667+
let found_span = expected_trait_ty.ty_to_def_id().and_then(|did| {
668+
self.tcx.hir.span_if_local(did)
669+
});
670+
671+
if let &TypeError::TupleSize(ref expected_found) = e {
672+
// Expected `|x| { }`, found `|x, y| { }`
673+
self.report_arg_count_mismatch(span,
674+
found_span,
675+
expected_found.expected,
676+
expected_found.found,
677+
expected_trait_ty.is_closure())
678+
} else if let &TypeError::Sorts(ref expected_found) = e {
679+
let expected = if let ty::TyTuple(tys, _) = expected_found.expected.sty {
680+
tys.len()
681+
} else {
682+
1
683+
};
684+
let found = if let ty::TyTuple(tys, _) = expected_found.found.sty {
685+
tys.len()
686+
} else {
687+
1
688+
};
689+
690+
if expected != found {
691+
// Expected `|| { }`, found `|x, y| { }`
692+
// Expected `fn(x) -> ()`, found `|| { }`
693+
self.report_arg_count_mismatch(span,
694+
found_span,
695+
expected,
696+
found,
697+
expected_trait_ty.is_closure())
698+
} else {
699+
self.report_type_argument_mismatch(span,
700+
found_span,
701+
expected_trait_ty,
702+
expected_trait_ref,
703+
actual_trait_ref,
704+
e)
705+
}
706+
} else {
707+
self.report_type_argument_mismatch(span,
708+
found_span,
709+
expected_trait_ty,
710+
expected_trait_ref,
711+
actual_trait_ref,
712+
e)
713+
}
673714
}
674715

675716
TraitNotObjectSafe(did) => {
@@ -681,6 +722,60 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
681722
self.note_obligation_cause(&mut err, obligation);
682723
err.emit();
683724
}
725+
726+
fn report_type_argument_mismatch(&self,
727+
span: Span,
728+
found_span: Option<Span>,
729+
expected_ty: Ty<'tcx>,
730+
expected_ref: ty::PolyTraitRef<'tcx>,
731+
found_ref: ty::PolyTraitRef<'tcx>,
732+
type_error: &TypeError<'tcx>)
733+
-> DiagnosticBuilder<'tcx>
734+
{
735+
let mut err = struct_span_err!(self.tcx.sess, span, E0281,
736+
"type mismatch: `{}` implements the trait `{}`, but the trait `{}` is required",
737+
expected_ty,
738+
expected_ref,
739+
found_ref);
740+
741+
err.span_label(span, &format!("{}", type_error));
742+
743+
if let Some(sp) = found_span {
744+
err.span_label(span, &format!("requires `{}`", found_ref));
745+
err.span_label(sp, &format!("implements `{}`", expected_ref));
746+
}
747+
748+
err
749+
}
750+
751+
fn report_arg_count_mismatch(&self,
752+
span: Span,
753+
found_span: Option<Span>,
754+
expected: usize,
755+
found: usize,
756+
is_closure: bool)
757+
-> DiagnosticBuilder<'tcx>
758+
{
759+
let mut err = struct_span_err!(self.tcx.sess, span, E0593,
760+
"{} takes {} argument{} but {} argument{} {} required",
761+
if is_closure { "closure" } else { "function" },
762+
found,
763+
if found == 1 { "" } else { "s" },
764+
expected,
765+
if expected == 1 { "" } else { "s" },
766+
if expected == 1 { "is" } else { "are" });
767+
768+
err.span_label(span, &format!("expected {} that takes {} argument{}",
769+
if is_closure { "closure" } else { "function" },
770+
expected,
771+
if expected == 1 { "" } else { "s" }));
772+
if let Some(span) = found_span {
773+
err.span_label(span, &format!("takes {} argument{}",
774+
found,
775+
if found == 1 { "" } else { "s" }));
776+
}
777+
err
778+
}
684779
}
685780

686781
impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {

src/librustc/ty/error.rs

+8-4
Original file line numberDiff line numberDiff line change
@@ -117,12 +117,16 @@ impl<'tcx> fmt::Display for TypeError<'tcx> {
117117
write!(f, "lifetimes do not intersect")
118118
}
119119
RegionsInsufficientlyPolymorphic(br, _, _) => {
120-
write!(f, "expected bound lifetime parameter {}, \
121-
found concrete lifetime", br)
120+
write!(f,
121+
"expected bound lifetime parameter{}{}, found concrete lifetime",
122+
if br.is_named() { " " } else { "" },
123+
br)
122124
}
123125
RegionsOverlyPolymorphic(br, _, _) => {
124-
write!(f, "expected concrete lifetime, \
125-
found bound lifetime parameter {}", br)
126+
write!(f,
127+
"expected concrete lifetime, found bound lifetime parameter{}{}",
128+
if br.is_named() { " " } else { "" },
129+
br)
126130
}
127131
Sorts(values) => ty::tls::with(|tcx| {
128132
report_maybe_different(f, values.expected.sort_string(tcx),

src/librustc/ty/sty.rs

+16
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,15 @@ pub enum BoundRegion {
6868
BrEnv,
6969
}
7070

71+
impl BoundRegion {
72+
pub fn is_named(&self) -> bool {
73+
match *self {
74+
BoundRegion::BrNamed(..) => true,
75+
_ => false,
76+
}
77+
}
78+
}
79+
7180
/// When a region changed from late-bound to early-bound when #32330
7281
/// was fixed, its `RegionParameterDef` will have one of these
7382
/// structures that we can use to give nicer errors.
@@ -1193,6 +1202,13 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
11931202
}
11941203
}
11951204

1205+
pub fn is_closure(&self) -> bool {
1206+
match self.sty {
1207+
TyClosure(..) => true,
1208+
_ => false,
1209+
}
1210+
}
1211+
11961212
pub fn is_integral(&self) -> bool {
11971213
match self.sty {
11981214
TyInfer(IntVar(_)) | TyInt(_) | TyUint(_) => true,

src/test/ui/mismatched_types/E0281.rs

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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+
fn foo<F: Fn(usize)>(x: F) { }
12+
13+
fn main() {
14+
foo(|y: String| { });
15+
//~^ ERROR E0281
16+
//~| ERROR E0281
17+
//~| NOTE implements
18+
//~| NOTE implements
19+
//~| NOTE requires
20+
//~| NOTE requires
21+
//~| NOTE expected usize, found struct `std::string::String`
22+
//~| NOTE expected usize, found struct `std::string::String`
23+
//~| NOTE required by `foo`
24+
//~| NOTE required by `foo`
25+
}
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
error[E0281]: type mismatch: `[closure@$DIR/E0281.rs:14:9: 14:24]` implements the trait `std::ops::Fn<(std::string::String,)>`, but the trait `std::ops::Fn<(usize,)>` is required
2+
--> $DIR/E0281.rs:14:5
3+
|
4+
14 | foo(|y: String| { });
5+
| ^^^ --------------- implements `std::ops::Fn<(std::string::String,)>`
6+
| |
7+
| requires `std::ops::Fn<(usize,)>`
8+
| expected usize, found struct `std::string::String`
9+
|
10+
= note: required by `foo`
11+
12+
error[E0281]: type mismatch: `[closure@$DIR/E0281.rs:14:9: 14:24]` implements the trait `std::ops::FnOnce<(std::string::String,)>`, but the trait `std::ops::FnOnce<(usize,)>` is required
13+
--> $DIR/E0281.rs:14:5
14+
|
15+
14 | foo(|y: String| { });
16+
| ^^^ --------------- implements `std::ops::FnOnce<(std::string::String,)>`
17+
| |
18+
| requires `std::ops::FnOnce<(usize,)>`
19+
| expected usize, found struct `std::string::String`
20+
|
21+
= note: required by `foo`
22+
23+
error: aborting due to 2 previous errors
24+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2017 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+
fn main() {
12+
[1, 2, 3].sort_by(|| panic!());
13+
[1, 2, 3].sort_by(|tuple| panic!());
14+
[1, 2, 3].sort_by(|(tuple, tuple2)| panic!());
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
error[E0593]: closure takes 0 arguments but 2 arguments are required
2+
--> $DIR/closure-arg-count.rs:12:15
3+
|
4+
12 | [1, 2, 3].sort_by(|| panic!());
5+
| ^^^^^^^ ----------- takes 0 arguments
6+
| |
7+
| expected closure that takes 2 arguments
8+
9+
error[E0593]: closure takes 0 arguments but 2 arguments are required
10+
--> $DIR/closure-arg-count.rs:12:15
11+
|
12+
12 | [1, 2, 3].sort_by(|| panic!());
13+
| ^^^^^^^ ----------- takes 0 arguments
14+
| |
15+
| expected closure that takes 2 arguments
16+
17+
error[E0593]: closure takes 1 argument but 2 arguments are required
18+
--> $DIR/closure-arg-count.rs:13:15
19+
|
20+
13 | [1, 2, 3].sort_by(|tuple| panic!());
21+
| ^^^^^^^ ---------------- takes 1 argument
22+
| |
23+
| expected closure that takes 2 arguments
24+
25+
error[E0593]: closure takes 1 argument but 2 arguments are required
26+
--> $DIR/closure-arg-count.rs:13:15
27+
|
28+
13 | [1, 2, 3].sort_by(|tuple| panic!());
29+
| ^^^^^^^ ---------------- takes 1 argument
30+
| |
31+
| expected closure that takes 2 arguments
32+
33+
error[E0308]: mismatched types
34+
--> $DIR/closure-arg-count.rs:14:24
35+
|
36+
14 | [1, 2, 3].sort_by(|(tuple, tuple2)| panic!());
37+
| ^^^^^^^^^^^^^^^ expected &{integer}, found tuple
38+
|
39+
= note: expected type `&{integer}`
40+
found type `(_, _)`
41+
42+
error[E0593]: closure takes 1 argument but 2 arguments are required
43+
--> $DIR/closure-arg-count.rs:14:15
44+
|
45+
14 | [1, 2, 3].sort_by(|(tuple, tuple2)| panic!());
46+
| ^^^^^^^ -------------------------- takes 1 argument
47+
| |
48+
| expected closure that takes 2 arguments
49+
50+
error[E0593]: closure takes 1 argument but 2 arguments are required
51+
--> $DIR/closure-arg-count.rs:14:15
52+
|
53+
14 | [1, 2, 3].sort_by(|(tuple, tuple2)| panic!());
54+
| ^^^^^^^ -------------------------- takes 1 argument
55+
| |
56+
| expected closure that takes 2 arguments
57+
58+
error: aborting due to 7 previous errors
59+
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
22
// file at the top-level directory of this distribution and at
33
// http://rust-lang.org/COPYRIGHT.
44
//
@@ -8,9 +8,12 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
fn foo<F: Fn()>(x: F) { }
11+
trait Foo {}
12+
13+
impl<T: Fn(&())> Foo for T {}
14+
15+
fn baz<T: Foo>(_: T) {}
1216

1317
fn main() {
14-
foo(|y| { }); //~ ERROR E0281
15-
//~^ ERROR E0281
18+
baz(|_| ());
1619
}

0 commit comments

Comments
 (0)