Skip to content

Commit

Permalink
Auto merge of #44735 - tirr-c:issue-42143, r=arielb1
Browse files Browse the repository at this point in the history
Friendlier error message for closure argument type mismatch

Rebased #42270.
Fixes #42143.

---

`test.rs`:

```rust
fn main() {
    foo(|_: i32, _: usize| ());
}

fn foo<F>(_: F) where F: Fn(&str, usize) {}
```

Before:

```
error[E0281]: type mismatch: `[[email protected]:2:9: 2:30]` implements the trait `std::ops::Fn<(i32, usize)>`, but the trait `for<'r> std::ops::Fn<(&'r str, usize)>` is required
 --> test.rs:2:5
  |
2 |     foo(|_: i32, _: usize| ());
  |     ^^^ --------------------- implements `std::ops::Fn<(i32, usize)>`
  |     |
  |     expected &str, found i32
  |     requires `for<'r> std::ops::Fn<(&'r str, usize)>`
  |
  = note: required by `foo`
```

After (early):

```
error[E0631]: type mismatch in closure arguments
 --> test.rs:2:5
  |
2 |     foo(|_: i32, _: usize| ());
  |     ^^^ --------------------- takes arguments of type `i32` and `usize`
  |     |
  |     expected arguments of type `&str` and `usize`
  |
  = note: required by `foo`
```

After (current):

```
error[E0631]: type mismatch in closure arguments
 --> test.rs:2:5
  |
2 |     foo(|_: i32, _: usize| ());
  |     ^^^ --------------------- found signature of `fn(i32, usize) -> _`
  |     |
  |     expected signature of `for<'r> fn(&'r str, usize) -> _`
  |
  = note: required by `foo`
```

~~Compiler output has been changed, and a few tests are failing. Help me writing/fixing tests!~~

r? @nikomatsakis
  • Loading branch information
bors committed Sep 26, 2017
2 parents 82ae968 + 1bfbfb2 commit 4b8bf39
Show file tree
Hide file tree
Showing 14 changed files with 245 additions and 119 deletions.
5 changes: 4 additions & 1 deletion src/librustc/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1105,11 +1105,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 @@ -2052,5 +2054,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 @@ -677,6 +677,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

0 comments on commit 4b8bf39

Please sign in to comment.