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

Friendlier error message for closure argument type mismatch #44735

Merged
merged 1 commit into from
Sep 26, 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
5 changes: 4 additions & 1 deletion src/librustc/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1139,11 +1139,13 @@ already specify all requirements that will be used for every type parameter.
"##,

E0281: r##"
#### Note: this error code is no longer emitted by the compiler.

You tried to supply a type which doesn't implement some trait in a location
which expected that trait. This error typically occurs when working with
`Fn`-based types. Erroneous code example:

```compile_fail,E0281
```compile-fail
fn foo<F: Fn(usize)>(x: F) { }

fn main() {
Expand Down Expand Up @@ -2086,5 +2088,6 @@ register_diagnostics! {
E0566, // conflicting representation hints
E0623, // lifetime mismatch where both parameters are anonymous regions
E0628, // generators cannot have explicit arguments
E0631, // type mismatch in closure arguments
E0637, // "'_" is not a valid lifetime bound
}
141 changes: 75 additions & 66 deletions src/librustc/traits/error_reporting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ use rustc::lint::builtin::EXTRA_REQUIREMENT_IN_IMPL;
use std::fmt;
use syntax::ast;
use ty::{self, AdtKind, ToPredicate, ToPolyTraitRef, Ty, TyCtxt, TypeFoldable};
use ty::error::{ExpectedFound, TypeError};
use ty::error::ExpectedFound;
use ty::fast_reject;
use ty::fold::TypeFolder;
use ty::subst::Subst;
Expand Down Expand Up @@ -711,7 +711,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
}
}

OutputTypeParameterMismatch(ref expected_trait_ref, ref actual_trait_ref, ref e) => {
OutputTypeParameterMismatch(ref expected_trait_ref, ref actual_trait_ref, _) => {
let expected_trait_ref = self.resolve_type_vars_if_possible(&*expected_trait_ref);
let actual_trait_ref = self.resolve_type_vars_if_possible(&*actual_trait_ref);
if actual_trait_ref.self_ty().references_error() {
Expand All @@ -722,48 +722,31 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
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 self_ty_count =
match expected_trait_ref.skip_binder().substs.type_at(1).sty {
ty::TyTuple(ref tys, _) => tys.len(),
_ => 1,
};
let found = if let ty::TyTuple(tys, _) = expected_found.found.sty {
tys.len()
} else {
1
let arg_ty_count =
match actual_trait_ref.skip_binder().substs.type_at(1).sty {
ty::TyTuple(ref tys, _) => tys.len(),
_ => 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)
}
if self_ty_count == arg_ty_count {
self.report_closure_arg_mismatch(span,
found_span,
expected_trait_ref,
actual_trait_ref)
} else {
self.report_type_argument_mismatch(span,
found_span,
expected_trait_ty,
expected_trait_ref,
actual_trait_ref,
e)
// Expected `|| { }`, found `|x, y| { }`
// Expected `fn(x) -> ()`, found `|| { }`
self.report_arg_count_mismatch(
span,
found_span,
arg_ty_count,
self_ty_count,
expected_trait_ty.is_closure()
)
}
}

Expand All @@ -784,31 +767,6 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
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>,
Expand Down Expand Up @@ -837,6 +795,57 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
}
err
}

fn report_closure_arg_mismatch(&self,
span: Span,
found_span: Option<Span>,
expected_ref: ty::PolyTraitRef<'tcx>,
found: ty::PolyTraitRef<'tcx>)
-> DiagnosticBuilder<'tcx>
{
fn build_fn_sig_string<'a, 'gcx, 'tcx>(tcx: ty::TyCtxt<'a, 'gcx, 'tcx>,
trait_ref: &ty::TraitRef<'tcx>) -> String {
let inputs = trait_ref.substs.type_at(1);
let sig = if let ty::TyTuple(inputs, _) = inputs.sty {
tcx.mk_fn_sig(
inputs.iter().map(|&x| x),
tcx.mk_infer(ty::TyVar(ty::TyVid { index: 0 })),
false,
hir::Unsafety::Normal,
::syntax::abi::Abi::Rust
)
} else {
tcx.mk_fn_sig(
::std::iter::once(inputs),
tcx.mk_infer(ty::TyVar(ty::TyVid { index: 0 })),
false,
hir::Unsafety::Normal,
::syntax::abi::Abi::Rust
)
};
format!("{}", ty::Binder(sig))
}

let argument_is_closure = expected_ref.skip_binder().substs.type_at(0).is_closure();
let mut err = struct_span_err!(self.tcx.sess, span, E0631,
"type mismatch in {} arguments",
if argument_is_closure { "closure" } else { "function" });

let found_str = format!(
"expected signature of `{}`",
build_fn_sig_string(self.tcx, found.skip_binder())
);
err.span_label(span, found_str);

let found_span = found_span.unwrap_or(span);
let expected_str = format!(
"found signature of `{}`",
build_fn_sig_string(self.tcx, expected_ref.skip_binder())
);
err.span_label(found_span, expected_str);

err
}
}

impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
Expand Down
6 changes: 6 additions & 0 deletions src/librustc/util/ppaux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,12 @@ impl<'tcx> fmt::Display for ty::Binder<ty::TraitRef<'tcx>> {
}
}

impl<'tcx> fmt::Display for ty::Binder<ty::FnSig<'tcx>> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
ty::tls::with(|tcx| in_binder(f, tcx, self, tcx.lift(self)))
}
}

impl<'tcx> fmt::Display for ty::Binder<ty::TraitPredicate<'tcx>> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
ty::tls::with(|tcx| in_binder(f, tcx, self, tcx.lift(self)))
Expand Down
13 changes: 0 additions & 13 deletions src/test/ui/mismatched_types/E0281.stderr

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,14 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

fn foo<F: Fn(usize)>(x: F) { }
#![feature(unboxed_closures)]

fn foo<F: Fn(usize)>(_: F) {}
fn bar<F: Fn<usize>>(_: 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`
fn f(_: u64) {}
foo(|_: isize| {});
bar(|_: isize| {});
foo(f);
bar(f);
}
44 changes: 44 additions & 0 deletions src/test/ui/mismatched_types/E0631.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
error[E0631]: type mismatch in closure arguments
--> $DIR/E0631.rs:17:5
|
17 | foo(|_: isize| {});
| ^^^ ------------- found signature of `fn(isize) -> _`
| |
| expected signature of `fn(usize) -> _`
|
= note: required by `foo`

error[E0631]: type mismatch in closure arguments
--> $DIR/E0631.rs:18:5
|
18 | bar(|_: isize| {});
| ^^^ ------------- found signature of `fn(isize) -> _`
| |
| expected signature of `fn(usize) -> _`
|
= note: required by `bar`

error[E0631]: type mismatch in function arguments
--> $DIR/E0631.rs:19:5
|
19 | foo(f);
| ^^^
| |
| expected signature of `fn(usize) -> _`
| found signature of `fn(u64) -> _`
|
= note: required by `foo`

error[E0631]: type mismatch in function arguments
--> $DIR/E0631.rs:20:5
|
20 | bar(f);
| ^^^
| |
| expected signature of `fn(usize) -> _`
| found signature of `fn(u64) -> _`
|
= note: required by `bar`

error: aborting due to 4 previous errors

4 changes: 4 additions & 0 deletions src/test/ui/mismatched_types/closure-arg-count.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(unboxed_closures)]

fn f<F: Fn<usize>>(_: F) {}
fn main() {
[1, 2, 3].sort_by(|| panic!());
[1, 2, 3].sort_by(|tuple| panic!());
[1, 2, 3].sort_by(|(tuple, tuple2)| panic!());
f(|| panic!());
}
28 changes: 19 additions & 9 deletions src/test/ui/mismatched_types/closure-arg-count.stderr
Original file line number Diff line number Diff line change
@@ -1,35 +1,45 @@
error[E0593]: closure takes 0 arguments but 2 arguments are required
--> $DIR/closure-arg-count.rs:12:15
--> $DIR/closure-arg-count.rs:15:15
|
12 | [1, 2, 3].sort_by(|| panic!());
15 | [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
--> $DIR/closure-arg-count.rs:16:15
|
13 | [1, 2, 3].sort_by(|tuple| panic!());
16 | [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
--> $DIR/closure-arg-count.rs:17:24
|
14 | [1, 2, 3].sort_by(|(tuple, tuple2)| panic!());
17 | [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
--> $DIR/closure-arg-count.rs:17:15
|
14 | [1, 2, 3].sort_by(|(tuple, tuple2)| panic!());
17 | [1, 2, 3].sort_by(|(tuple, tuple2)| panic!());
| ^^^^^^^ -------------------------- takes 1 argument
| |
| expected closure that takes 2 arguments

error: aborting due to 4 previous errors
error[E0593]: closure takes 0 arguments but 1 argument is required
--> $DIR/closure-arg-count.rs:18:5
|
18 | f(|| panic!());
| ^ ----------- takes 0 arguments
| |
| expected closure that takes 1 argument
|
= note: required by `f`

error: aborting due to 5 previous errors

21 changes: 21 additions & 0 deletions src/test/ui/mismatched_types/closure-arg-type-mismatch.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// 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() {
let a = [(1u32, 2u32)];
a.iter().map(|_: (u32, u32)| 45);
a.iter().map(|_: &(u16, u16)| 45);
a.iter().map(|_: (u16, u16)| 45);
}

fn baz<F: Fn(*mut &u32)>(_: F) {}
fn _test<'a>(f: fn(*mut &'a u32)) {
baz(f);
}
Loading