Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clean up callable type mismatch errors #41488

Merged
merged 4 commits into from
May 2, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 21 additions & 6 deletions src/librustc/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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##"
Expand Down Expand Up @@ -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| { });
}
```
"##,

}


Expand Down
111 changes: 103 additions & 8 deletions src/librustc/traits/error_reporting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) => {
Expand All @@ -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> {
Expand Down
12 changes: 8 additions & 4 deletions src/librustc/ty/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
16 changes: 16 additions & 0 deletions src/librustc/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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,
Expand Down
25 changes: 25 additions & 0 deletions src/test/ui/mismatched_types/E0281.rs
Original file line number Diff line number Diff line change
@@ -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`
}
24 changes: 24 additions & 0 deletions src/test/ui/mismatched_types/E0281.stderr
Original file line number Diff line number Diff line change
@@ -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

15 changes: 15 additions & 0 deletions src/test/ui/mismatched_types/closure-arg-count.rs
Original file line number Diff line number Diff line change
@@ -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!());
}
59 changes: 59 additions & 0 deletions src/test/ui/mismatched_types/closure-arg-count.stderr
Original file line number Diff line number Diff line change
@@ -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

Original file line number Diff line number Diff line change
@@ -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.
//
Expand All @@ -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(|_| ());
}
Loading