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.
This commit is contained in:
bors 2017-05-02 09:41:39 +00:00
commit 50517d58a2
16 changed files with 399 additions and 24 deletions

View File

@ -1049,18 +1049,19 @@ which expected that trait. This error typically occurs when working with
`Fn`-based types. Erroneous code example:
```compile_fail,E0281
fn foo<F: Fn()>(x: F) { }
fn foo<F: Fn(usize)>(x: F) { }
fn main() {
// type mismatch: the type ... implements the trait `core::ops::Fn<(_,)>`,
// but the trait `core::ops::Fn<()>` is required (expected (), found tuple
// type mismatch: ... implements the trait `core::ops::Fn<(String,)>`,
// but the trait `core::ops::Fn<(usize,)>` is required
// [E0281]
foo(|y| { });
foo(|y: String| { });
}
```
The issue in this case is that `foo` is defined as accepting a `Fn` with no
arguments, but the closure we attempted to pass to it requires one argument.
The issue in this case is that `foo` is defined as accepting a `Fn` with one
argument of type `String`, but the closure we attempted to pass to it requires
one arguments of type `usize`.
"##,
E0282: r##"
@ -1807,6 +1808,20 @@ makes a difference in practice.)
[rfc401]: https://github.com/rust-lang/rfcs/blob/master/text/0401-coercions.md
"##,
E0593: r##"
You tried to supply an `Fn`-based type with an incorrect number of arguments
than what was expected. Erroneous code example:
```compile_fail,E0593
fn foo<F: Fn()>(x: F) { }
fn main() {
// [E0593] closure takes 1 argument but 0 arguments are required
foo(|y| { });
}
```
"##,
}

View File

@ -35,7 +35,7 @@ use rustc::lint::builtin::EXTRA_REQUIREMENT_IN_IMPL;
use std::fmt;
use syntax::ast::{self, NodeId};
use ty::{self, AdtKind, ToPredicate, ToPolyTraitRef, Ty, TyCtxt, TypeFoldable, TyInfer, TyVar};
use ty::error::ExpectedFound;
use ty::error::{ExpectedFound, TypeError};
use ty::fast_reject;
use ty::fold::TypeFolder;
use ty::subst::Subst;
@ -663,13 +663,54 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
if actual_trait_ref.self_ty().references_error() {
return;
}
struct_span_err!(self.tcx.sess, span, E0281,
"type mismatch: the type `{}` implements the trait `{}`, \
but the trait `{}` is required ({})",
expected_trait_ref.self_ty(),
expected_trait_ref,
actual_trait_ref,
e)
let expected_trait_ty = expected_trait_ref.self_ty();
let found_span = expected_trait_ty.ty_to_def_id().and_then(|did| {
self.tcx.hir.span_if_local(did)
});
if let &TypeError::TupleSize(ref expected_found) = e {
// Expected `|x| { }`, found `|x, y| { }`
self.report_arg_count_mismatch(span,
found_span,
expected_found.expected,
expected_found.found,
expected_trait_ty.is_closure())
} else if let &TypeError::Sorts(ref expected_found) = e {
let expected = if let ty::TyTuple(tys, _) = expected_found.expected.sty {
tys.len()
} else {
1
};
let found = if let ty::TyTuple(tys, _) = expected_found.found.sty {
tys.len()
} else {
1
};
if expected != found {
// Expected `|| { }`, found `|x, y| { }`
// Expected `fn(x) -> ()`, found `|| { }`
self.report_arg_count_mismatch(span,
found_span,
expected,
found,
expected_trait_ty.is_closure())
} else {
self.report_type_argument_mismatch(span,
found_span,
expected_trait_ty,
expected_trait_ref,
actual_trait_ref,
e)
}
} else {
self.report_type_argument_mismatch(span,
found_span,
expected_trait_ty,
expected_trait_ref,
actual_trait_ref,
e)
}
}
TraitNotObjectSafe(did) => {
@ -681,6 +722,60 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
self.note_obligation_cause(&mut err, obligation);
err.emit();
}
fn report_type_argument_mismatch(&self,
span: Span,
found_span: Option<Span>,
expected_ty: Ty<'tcx>,
expected_ref: ty::PolyTraitRef<'tcx>,
found_ref: ty::PolyTraitRef<'tcx>,
type_error: &TypeError<'tcx>)
-> DiagnosticBuilder<'tcx>
{
let mut err = struct_span_err!(self.tcx.sess, span, E0281,
"type mismatch: `{}` implements the trait `{}`, but the trait `{}` is required",
expected_ty,
expected_ref,
found_ref);
err.span_label(span, &format!("{}", type_error));
if let Some(sp) = found_span {
err.span_label(span, &format!("requires `{}`", found_ref));
err.span_label(sp, &format!("implements `{}`", expected_ref));
}
err
}
fn report_arg_count_mismatch(&self,
span: Span,
found_span: Option<Span>,
expected: usize,
found: usize,
is_closure: bool)
-> DiagnosticBuilder<'tcx>
{
let mut err = struct_span_err!(self.tcx.sess, span, E0593,
"{} takes {} argument{} but {} argument{} {} required",
if is_closure { "closure" } else { "function" },
found,
if found == 1 { "" } else { "s" },
expected,
if expected == 1 { "" } else { "s" },
if expected == 1 { "is" } else { "are" });
err.span_label(span, &format!("expected {} that takes {} argument{}",
if is_closure { "closure" } else { "function" },
expected,
if expected == 1 { "" } else { "s" }));
if let Some(span) = found_span {
err.span_label(span, &format!("takes {} argument{}",
found,
if found == 1 { "" } else { "s" }));
}
err
}
}
impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {

View File

@ -117,12 +117,16 @@ impl<'tcx> fmt::Display for TypeError<'tcx> {
write!(f, "lifetimes do not intersect")
}
RegionsInsufficientlyPolymorphic(br, _, _) => {
write!(f, "expected bound lifetime parameter {}, \
found concrete lifetime", br)
write!(f,
"expected bound lifetime parameter{}{}, found concrete lifetime",
if br.is_named() { " " } else { "" },
br)
}
RegionsOverlyPolymorphic(br, _, _) => {
write!(f, "expected concrete lifetime, \
found bound lifetime parameter {}", br)
write!(f,
"expected concrete lifetime, found bound lifetime parameter{}{}",
if br.is_named() { " " } else { "" },
br)
}
Sorts(values) => ty::tls::with(|tcx| {
report_maybe_different(f, values.expected.sort_string(tcx),

View File

@ -68,6 +68,15 @@ pub enum BoundRegion {
BrEnv,
}
impl BoundRegion {
pub fn is_named(&self) -> bool {
match *self {
BoundRegion::BrNamed(..) => true,
_ => false,
}
}
}
/// When a region changed from late-bound to early-bound when #32330
/// was fixed, its `RegionParameterDef` will have one of these
/// structures that we can use to give nicer errors.
@ -1193,6 +1202,13 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
}
}
pub fn is_closure(&self) -> bool {
match self.sty {
TyClosure(..) => true,
_ => false,
}
}
pub fn is_integral(&self) -> bool {
match self.sty {
TyInfer(IntVar(_)) | TyInt(_) | TyUint(_) => true,

View File

@ -0,0 +1,25 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
fn foo<F: Fn(usize)>(x: F) { }
fn main() {
foo(|y: String| { });
//~^ ERROR E0281
//~| ERROR E0281
//~| NOTE implements
//~| NOTE implements
//~| NOTE requires
//~| NOTE requires
//~| NOTE expected usize, found struct `std::string::String`
//~| NOTE expected usize, found struct `std::string::String`
//~| NOTE required by `foo`
//~| NOTE required by `foo`
}

View File

@ -0,0 +1,24 @@
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
--> $DIR/E0281.rs:14:5
|
14 | foo(|y: String| { });
| ^^^ --------------- implements `std::ops::Fn<(std::string::String,)>`
| |
| requires `std::ops::Fn<(usize,)>`
| expected usize, found struct `std::string::String`
|
= note: required by `foo`
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
--> $DIR/E0281.rs:14:5
|
14 | foo(|y: String| { });
| ^^^ --------------- implements `std::ops::FnOnce<(std::string::String,)>`
| |
| requires `std::ops::FnOnce<(usize,)>`
| expected usize, found struct `std::string::String`
|
= note: required by `foo`
error: aborting due to 2 previous errors

View File

@ -0,0 +1,15 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
fn main() {
[1, 2, 3].sort_by(|| panic!());
[1, 2, 3].sort_by(|tuple| panic!());
[1, 2, 3].sort_by(|(tuple, tuple2)| panic!());
}

View File

@ -0,0 +1,59 @@
error[E0593]: closure takes 0 arguments but 2 arguments are required
--> $DIR/closure-arg-count.rs:12:15
|
12 | [1, 2, 3].sort_by(|| panic!());
| ^^^^^^^ ----------- takes 0 arguments
| |
| expected closure that takes 2 arguments
error[E0593]: closure takes 0 arguments but 2 arguments are required
--> $DIR/closure-arg-count.rs:12:15
|
12 | [1, 2, 3].sort_by(|| panic!());
| ^^^^^^^ ----------- takes 0 arguments
| |
| expected closure that takes 2 arguments
error[E0593]: closure takes 1 argument but 2 arguments are required
--> $DIR/closure-arg-count.rs:13:15
|
13 | [1, 2, 3].sort_by(|tuple| panic!());
| ^^^^^^^ ---------------- takes 1 argument
| |
| expected closure that takes 2 arguments
error[E0593]: closure takes 1 argument but 2 arguments are required
--> $DIR/closure-arg-count.rs:13:15
|
13 | [1, 2, 3].sort_by(|tuple| panic!());
| ^^^^^^^ ---------------- takes 1 argument
| |
| expected closure that takes 2 arguments
error[E0308]: mismatched types
--> $DIR/closure-arg-count.rs:14:24
|
14 | [1, 2, 3].sort_by(|(tuple, tuple2)| panic!());
| ^^^^^^^^^^^^^^^ expected &{integer}, found tuple
|
= note: expected type `&{integer}`
found type `(_, _)`
error[E0593]: closure takes 1 argument but 2 arguments are required
--> $DIR/closure-arg-count.rs:14:15
|
14 | [1, 2, 3].sort_by(|(tuple, tuple2)| panic!());
| ^^^^^^^ -------------------------- takes 1 argument
| |
| expected closure that takes 2 arguments
error[E0593]: closure takes 1 argument but 2 arguments are required
--> $DIR/closure-arg-count.rs:14:15
|
14 | [1, 2, 3].sort_by(|(tuple, tuple2)| panic!());
| ^^^^^^^ -------------------------- takes 1 argument
| |
| expected closure that takes 2 arguments
error: aborting due to 7 previous errors

View File

@ -1,4 +1,4 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
@ -8,9 +8,12 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
fn foo<F: Fn()>(x: F) { }
trait Foo {}
impl<T: Fn(&())> Foo for T {}
fn baz<T: Foo>(_: T) {}
fn main() {
foo(|y| { }); //~ ERROR E0281
//~^ ERROR E0281
baz(|_| ());
}

View File

@ -0,0 +1,24 @@
error[E0271]: type mismatch resolving `for<'r> <[closure@$DIR/closure-mismatch.rs:18:9: 18:15] as std::ops::FnOnce<(&'r (),)>>::Output == ()`
--> $DIR/closure-mismatch.rs:18:5
|
18 | baz(|_| ());
| ^^^ expected bound lifetime parameter, found concrete lifetime
|
= note: concrete lifetime that was found is lifetime '_#0r
= note: required because of the requirements on the impl of `Foo` for `[closure@$DIR/closure-mismatch.rs:18:9: 18:15]`
= note: required by `baz`
error[E0281]: type mismatch: `[closure@$DIR/closure-mismatch.rs:18:9: 18:15]` implements the trait `std::ops::Fn<(_,)>`, but the trait `for<'r> std::ops::Fn<(&'r (),)>` is required
--> $DIR/closure-mismatch.rs:18:5
|
18 | baz(|_| ());
| ^^^ ------ implements `std::ops::Fn<(_,)>`
| |
| requires `for<'r> std::ops::Fn<(&'r (),)>`
| expected concrete lifetime, found bound lifetime parameter
|
= note: required because of the requirements on the impl of `Foo` for `[closure@$DIR/closure-mismatch.rs:18:9: 18:15]`
= note: required by `baz`
error: aborting due to 2 previous errors

View File

@ -19,9 +19,13 @@ fn apply<T, F>(t: T, f: F) where F: FnOnce(T) {
fn main() {
apply(&3, takes_imm);
apply(&3, takes_mut);
//~^ ERROR (types differ in mutability)
//~^ ERROR type mismatch
//~| NOTE types differ in mutability
//~| NOTE required by `apply`
apply(&mut 3, takes_mut);
apply(&mut 3, takes_imm);
//~^ ERROR (types differ in mutability)
//~^ ERROR type mismatch
//~| NOTE types differ in mutability
//~| NOTE required by `apply`
}

View File

@ -0,0 +1,18 @@
error[E0281]: type mismatch: `fn(&mut isize) {takes_mut}` implements the trait `for<'r> std::ops::FnOnce<(&'r mut isize,)>`, but the trait `std::ops::FnOnce<(&{integer},)>` is required
--> $DIR/fn-variance-1.rs:21:5
|
21 | apply(&3, takes_mut);
| ^^^^^ types differ in mutability
|
= note: required by `apply`
error[E0281]: type mismatch: `fn(&isize) {takes_imm}` implements the trait `for<'r> std::ops::FnOnce<(&'r isize,)>`, but the trait `std::ops::FnOnce<(&mut {integer},)>` is required
--> $DIR/fn-variance-1.rs:27:5
|
27 | apply(&mut 3, takes_imm);
| ^^^^^ types differ in mutability
|
= note: required by `apply`
error: aborting due to 2 previous errors

View File

@ -18,4 +18,11 @@ fn main() {
//~^ ERROR no method named `count`
//~| ERROR E0281
//~| ERROR E0281
//~| NOTE expected &str, found str
//~| NOTE expected &str, found str
//~| NOTE implements
//~| NOTE implements
//~| NOTE requires
//~| NOTE requires
//~| NOTE the method `count` exists but the following trait bounds
}

View File

@ -0,0 +1,28 @@
error: no method named `count` found for type `std::iter::Filter<std::iter::Fuse<std::iter::Once<&str>>, [closure@$DIR/issue-36053-2.rs:17:39: 17:53]>` in the current scope
--> $DIR/issue-36053-2.rs:17:55
|
17 | once::<&str>("str").fuse().filter(|a: &str| true).count();
| ^^^^^
|
= note: the method `count` exists but the following trait bounds were not satisfied: `[closure@$DIR/issue-36053-2.rs:17:39: 17:53] : std::ops::FnMut<(&_,)>`, `std::iter::Filter<std::iter::Fuse<std::iter::Once<&str>>, [closure@$DIR/issue-36053-2.rs:17:39: 17:53]> : std::iter::Iterator`
error[E0281]: type mismatch: `[closure@$DIR/issue-36053-2.rs:17:39: 17:53]` implements the trait `for<'r> std::ops::FnMut<(&'r str,)>`, but the trait `for<'r> std::ops::FnMut<(&'r &str,)>` is required
--> $DIR/issue-36053-2.rs:17:32
|
17 | once::<&str>("str").fuse().filter(|a: &str| true).count();
| ^^^^^^ -------------- implements `for<'r> std::ops::FnMut<(&'r str,)>`
| |
| requires `for<'r> std::ops::FnMut<(&'r &str,)>`
| expected &str, found str
error[E0281]: type mismatch: `[closure@$DIR/issue-36053-2.rs:17:39: 17:53]` implements the trait `for<'r> std::ops::FnOnce<(&'r str,)>`, but the trait `for<'r> std::ops::FnOnce<(&'r &str,)>` is required
--> $DIR/issue-36053-2.rs:17:32
|
17 | once::<&str>("str").fuse().filter(|a: &str| true).count();
| ^^^^^^ -------------- implements `for<'r> std::ops::FnOnce<(&'r str,)>`
| |
| requires `for<'r> std::ops::FnOnce<(&'r &str,)>`
| expected &str, found str
error: aborting due to 3 previous errors

View File

@ -20,8 +20,16 @@ fn call_it<F:FnMut(isize,isize)->isize>(y: isize, mut f: F) -> isize {
pub fn main() {
let f = to_fn_mut(|x: usize, y: isize| -> isize { (x as isize) + y });
//~^ NOTE implements
//~| NOTE implements
let z = call_it(3, f);
//~^ ERROR type mismatch
//~| ERROR type mismatch
//~| NOTE expected isize, found usize
//~| NOTE expected isize, found usize
//~| NOTE requires
//~| NOTE requires
//~| NOTE required by `call_it`
//~| NOTE required by `call_it`
println!("{}", z);
}

View File

@ -0,0 +1,30 @@
error[E0281]: type mismatch: `[closure@$DIR/unboxed-closures-vtable-mismatch.rs:22:23: 22:73]` implements the trait `std::ops::FnMut<(usize, isize)>`, but the trait `std::ops::FnMut<(isize, isize)>` is required
--> $DIR/unboxed-closures-vtable-mismatch.rs:25:13
|
22 | let f = to_fn_mut(|x: usize, y: isize| -> isize { (x as isize) + y });
| -------------------------------------------------- implements `std::ops::FnMut<(usize, isize)>`
...
25 | let z = call_it(3, f);
| ^^^^^^^
| |
| requires `std::ops::FnMut<(isize, isize)>`
| expected isize, found usize
|
= note: required by `call_it`
error[E0281]: type mismatch: `[closure@$DIR/unboxed-closures-vtable-mismatch.rs:22:23: 22:73]` implements the trait `std::ops::FnOnce<(usize, isize)>`, but the trait `std::ops::FnOnce<(isize, isize)>` is required
--> $DIR/unboxed-closures-vtable-mismatch.rs:25:13
|
22 | let f = to_fn_mut(|x: usize, y: isize| -> isize { (x as isize) + y });
| -------------------------------------------------- implements `std::ops::FnOnce<(usize, isize)>`
...
25 | let z = call_it(3, f);
| ^^^^^^^
| |
| requires `std::ops::FnOnce<(isize, isize)>`
| expected isize, found usize
|
= note: required by `call_it`
error: aborting due to 2 previous errors