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

Conversation

Projects
None yet
7 participants
@tirr-c
Copy link
Contributor

tirr-c commented Sep 21, 2017

Rebased #42270.
Fixes #42143.


test.rs:

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

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

Before:

error[E0281]: type mismatch: `[closure@test.rs: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

@rust-highfive

This comment has been minimized.

Copy link
Collaborator

rust-highfive commented Sep 21, 2017

Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @nikomatsakis (or someone else) soon.

If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes.

Please see the contribution instructions for more information.

@@ -730,40 +730,35 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
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 {

This comment has been minimized.

@arielb1

arielb1 Sep 21, 2017

Contributor

You're still matching against the expected/found, which could lead to this bogusly triggering when one of the arguments is a tuple - when you have something like fn((u32, u32)): Fn((u32,)).

There's no reason to do so - you have the trait-refs in expected_trait_ref and actual_trait_ref.

You can get the argument length of each argument in this way:

// skipping the binder is ok because we only care if this is a tuple
let arg_len = match X_trait_ref.skip_binder().substs.type_at(1) {
    ty::TyTuple(ref tys) => Some(tys.len()),
    _ => None
};
@tirr-c
Copy link
Contributor Author

tirr-c left a comment

I have a few questions. I've contributed to Rust a little, but still not sure how to write compiler code...

ty::TyTuple(args, _) => {
let len = args.len();
if len == 0 {
return String::from("no arguments");

This comment has been minimized.

@tirr-c

tirr-c Sep 21, 2017

Author Contributor

Is this code unreachable? I mean, will report_closure_arg_mismatch be called even when one of the Fn traits has zero arguments?

This comment has been minimized.

@arielb1

arielb1 Sep 21, 2017

Contributor

Why aren't you calling report_type_argument_mismatch?

This comment has been minimized.

@arielb1

arielb1 Sep 21, 2017

Contributor

ok I see

This comment has been minimized.

@arielb1

arielb1 Sep 21, 2017

Contributor

I think you won't see this error message - no arguments, no place for a type error.

if len == 1 { "" } else { "s" },
type_list_string)
},
_ => panic!(),

This comment has been minimized.

@tirr-c

tirr-c Sep 21, 2017

Author Contributor

Is it fine to use standard panic macro like panic! or unreachable! to signal compiler bugs? (panic! here is a mistake, I should have used unreachable! here)

This comment has been minimized.

@arielb1

arielb1 Sep 21, 2017

Contributor

An unreachable!() is ok-ish for local things, but it is recommended to use bug! or span_bug!(span, "error") - span_bug also displays the span of the error, which makes for easier debugging.

move |trait_ref| build_args_type_string(span, trait_ref)
);
err.span_label(span, format!("expected {}", found_arg_list.skip_binder()));
if let Some(sp) = found_span {

This comment has been minimized.

@arielb1

arielb1 Sep 21, 2017

Contributor

You should display the "takes" even when you don't have a span - aka fn pointers:

fn foo(f: fn((u32, u32))) {
    a.iter().map(f)
}

This comment has been minimized.

@arielb1

arielb1 Sep 21, 2017

Contributor

I think you should add a test for each case with fn pointer - arg count mismatch, same arg count but type-error, higher-ranked error, F: Fn<u32> bad bound.

This comment has been minimized.

@arielb1

arielb1 Sep 21, 2017

Contributor

Higher-ranked test for fn pointers:

fn foo<F: Fn(*mut &u32)>(_f: F) {}
fn bar<'a>(f: fn(*mut &'a u32)) { foo(f); }

Higher-ranked test for closures: src/test/ui/mismatched_types/closure-mismatch.rs (already exists)

Just check that the error message is sane.

This comment has been minimized.

@tirr-c

tirr-c Sep 21, 2017

Author Contributor

Can I use note here?

This comment has been minimized.

@nikomatsakis

nikomatsakis Sep 21, 2017

Contributor

With respect to the span specifically, probably just something like this would be fine:

let found_span = found_span.unwrap_or(span);

i.e., if we don't have a better span, display both messages at the same place.

@nikomatsakis

This comment has been minimized.

Copy link
Contributor

nikomatsakis commented Sep 21, 2017

@tirr-c this looks awesome btw!

@tirr-c

This comment has been minimized.

Copy link
Contributor Author

tirr-c commented Sep 21, 2017

Tried @arielb1's test code, and I get two errors.

#![feature(unboxed_closures)]

fn main() {}
fn foo<F: Fn(*mut &u32)>(_f: F) {}
fn bar<'a>(f: fn(*mut &'a u32)) { foo(f); }

(ignore feature line there)

error[E0631]: type mismatch in closure arguments
 --> test.rs:5:35
  |
5 | fn bar<'a>(f: fn(*mut &'a u32)) { foo(f); }
  |                                   ^^^
  |                                   |
  |                                   expected argument of type `*mut &u32`
  |                                   takes argument of type `*mut &'a u32`
  |
  = note: required by `foo`

error[E0271]: type mismatch resolving `for<'r> <fn(*mut &'a u32) as std::ops::FnOnce<(*mut &'r u32,)>>::Output == ()`
 --> test.rs:5:35
  |
5 | fn bar<'a>(f: fn(*mut &'a u32)) { foo(f); }
  |                                   ^^^ expected bound lifetime parameter, found concrete lifetime
  |
  = note: required by `foo`

error: aborting due to 2 previous errors

Is this okay?

@arielb1

This comment has been minimized.

Copy link
Contributor

arielb1 commented Sep 21, 2017

Is this okay?

Your error message says "closure" for the function pointer. You should make it say "function" (you can use expected_trait_ty.is_closure() to check whether it's a function.

@arielb1

This comment has been minimized.

Copy link
Contributor

arielb1 commented Sep 21, 2017

Other than that the error messages look sane. Just be sure to add a UI test so I can see them before giving the r+.

Otherwise LGTM.

@tirr-c

This comment has been minimized.

Copy link
Contributor Author

tirr-c commented Sep 21, 2017

I've removed the test mismatched_types/E0281 because most of the E0281 errors are replaced by E0631 which is added in this PR, and mismatched_types/closure-arg-type-mismatch contains tests for both E0281 and E0631.

@@ -1,25 +0,0 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT

This comment has been minimized.

@arielb1

arielb1 Sep 21, 2017

Contributor

If E0281 still exists, please write a test that reproduces it. That's the Fn<usize> test.

@@ -7,14 +7,13 @@ error[E0271]: type mismatch resolving `for<'r> <[closure@$DIR/closure-mismatch.r
= 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
error[E0631]: type mismatch in closure arguments

This comment has been minimized.

@arielb1

arielb1 Sep 21, 2017

Contributor

This erases the binder. I don't like this error message.

This comment has been minimized.

@estebank

estebank Sep 22, 2017

Contributor

@arielb1 the expected and found signatures are in the labels now, I'm not sure if that was the case when you left this comment.

@arielb1

This comment has been minimized.

Copy link
Contributor

arielb1 commented Sep 21, 2017

Please add the test for E0281 back - all error codes are supposed to have a test for them, and you can similarly add a test for E0631 with the name E0631.rs. We also need to think of how to handle closure-mismatch.

@arielb1

This comment has been minimized.

Copy link
Contributor

arielb1 commented Sep 21, 2017

[01:05:30] failures:
[01:05:30] 
[01:05:30] ---- /checkout/obj/build/x86_64-unknown-linux-gnu/test/error-index.md - Rust_Compiler_Error_Index (line 4928) stdout ----
[01:05:30] 	error[E0631]: type mismatch in closure arguments
[01:05:30]  --> /checkout/obj/build/x86_64-unknown-linux-gnu/test/error-index.md:7:5
[01:05:30]   |
[01:05:30] 7 |     foo(|y: String| { });
[01:05:30]   |     ^^^ --------------- takes argument of type `std::string::String`
[01:05:30]   |     |
[01:05:30]   |     expected argument of type `usize`
[01:05:30]   |
[01:05:30]   = note: required by `foo`
[01:05:30] 
[01:05:30] thread 'rustc' panicked at 'Some expected error codes were not found: ["E0281"]', /checkout/src/librustdoc/test.rs:286:8
[01:05:30] 
[01:05:30] 
[01:05:30] failures:
[01:05:30]     /checkout/obj/build/x86_64-unknown-linux-gnu/test/error-index.md - Rust_Compiler_Error_Index (line 4928)
[01:05:30] 
[01:05:30] test result: FAILED. 659 passed; 1 failed; 19 ignored; 0 measured; 0 filtered out
[01:05:30] 
[01:05:30] 

there's another doctest for E0281 in

E0281: r##"
, you need to fix it

@nikomatsakis

This comment has been minimized.

Copy link
Contributor

nikomatsakis commented Sep 21, 2017

@tirr-c

This comment has been minimized.

Copy link
Contributor Author

tirr-c commented Sep 21, 2017

Should pass the test, but the documentation needs to be rewritten now I think...

@arielb1

This comment has been minimized.

Copy link
Contributor

arielb1 commented Sep 21, 2017

now you just need to use fn in the error messages.

@tirr-c

This comment has been minimized.

Copy link
Contributor Author

tirr-c commented Sep 22, 2017

error[E0631]: type mismatch in closure arguments
  --> src/test/ui/mismatched_types/closure-mismatch.rs:18:5
   |
18 |     baz(|_| ());
   |     ^^^ ------ found signature of fn(_) -> _
   |     |
   |     expected signature of for<'r> fn(&'r ()) -> _
   |
   = note: required because of the requirements on the impl of `Foo` for `[closure@src/test/ui/mismatched_types/closure-mismatch.rs:18:9: 18:15]`
   = note: required by `baz`
@tirr-c

This comment has been minimized.

Copy link
Contributor Author

tirr-c commented Sep 22, 2017

Error message from the test for Fn<usize> became worse.

Before:

error[E0593]: closure takes 0 arguments but 1 argument is required
  --> src/test/ui/mismatched_types/closure-arg-type-mismatch.rs:26:5
   |
26 |     bar(|| {});
   |     ^^^ ----- takes 0 arguments
   |     |
   |     expected closure that takes 1 argument
   |
   = note: required by `bar`

After:

error[E0281]: type mismatch: `[closure@src/test/ui/mismatched_types/closure-arg-type-mismatch.rs:26:9: 26:14]` implements the trait `std::ops::Fn<()>`, but the trait `std::ops::Fn<usize>` is required
  --> src/test/ui/mismatched_types/closure-arg-type-mismatch.rs:26:5
   |
26 |     bar(|| {});
   |     ^^^ ----- implements `std::ops::Fn<()>`
   |     |
   |     expected usize, found ()
   |     requires `std::ops::Fn<usize>`
   |
   = note: required by `bar`
@tirr-c

This comment has been minimized.

Copy link
Contributor Author

tirr-c commented Sep 22, 2017

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

E0281, the original error code, is totally replaced by E0631. I left E0281 in the error code index, though.

@tirr-c tirr-c force-pushed the tirr-c:issue-42143 branch from 6764934 to 135545a Sep 22, 2017

@bors

This comment has been minimized.

Copy link
Contributor

bors commented Sep 22, 2017

☔️ The latest upstream changes (presumably #44691) made this pull request unmergeable. Please resolve the merge conflicts.

@tirr-c tirr-c force-pushed the tirr-c:issue-42143 branch from 135545a to 29e5db0 Sep 22, 2017

--> $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,)>`
| ^^^^^^ -------------- found signature of for<'r> fn(&'r str) -> _

This comment has been minimized.

@nikomatsakis

nikomatsakis Sep 22, 2017

Contributor

Nit: we should enclose the signature in backticks:

 +   |                                ^^^^^^ -------------- found signature of `for<'r> fn(&'r str) -> _`

@tirr-c tirr-c force-pushed the tirr-c:issue-42143 branch from 29e5db0 to cc56688 Sep 23, 2017

Print fn signature when there is closure argument type mismatch
Fixes #42143.
E0281 is totally replaced by E0631. UI tests are updated accordingly.

@tirr-c tirr-c force-pushed the tirr-c:issue-42143 branch from cc56688 to 1bfbfb2 Sep 23, 2017

@arielb1

This comment has been minimized.

Copy link
Contributor

arielb1 commented Sep 24, 2017

That's cool.

@bors r+

@bors

This comment has been minimized.

Copy link
Contributor

bors commented Sep 24, 2017

📌 Commit 1bfbfb2 has been approved by arielb1

@bors

This comment has been minimized.

Copy link
Contributor

bors commented Sep 26, 2017

⌛️ Testing commit 1bfbfb2 with merge 4b8bf39...

bors added a commit that referenced this pull request Sep 26, 2017

Auto merge of #44735 - tirr-c:issue-42143, r=arielb1
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: `[closure@test.rs: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
@bors

This comment has been minimized.

Copy link
Contributor

bors commented Sep 26, 2017

☀️ Test successful - status-appveyor, status-travis
Approved by: arielb1
Pushing 4b8bf39 to master...

@bors bors merged commit 1bfbfb2 into rust-lang:master Sep 26, 2017

2 checks passed

continuous-integration/travis-ci/pr The Travis CI build passed
Details
homu Test successful
Details
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.