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

Fix wrong report on closure args mismatch when a ref is expected but not found #42270

Closed
wants to merge 2 commits into from

Conversation

Emilgardis
Copy link
Contributor

@Emilgardis Emilgardis commented May 27, 2017

Fixes #42143.

Also adds test for this case.

@rust-highfive
Copy link
Collaborator

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.

@Mark-Simulacrum Mark-Simulacrum added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label May 28, 2017
@arielb1
Copy link
Contributor

arielb1 commented May 29, 2017

@Emilgardis

I would personally prefer to issue the error message when you find that the obligation & closure trait refs are function traits whose first arguments are tuples of different arg-counts, rather than trying to match against type errors.

@alexcrichton alexcrichton added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jun 1, 2017
@nikomatsakis
Copy link
Contributor

nikomatsakis commented Jun 1, 2017

I think this is the same thing that @arielb1 was saying, but I feel like we could do a lot better with this error message. That is, ideally, we would tell the user that the closure is declared with argument type (u32, u32), but the iterator is providing a &(u32, u32). It's a bit tricky to find just the right phrasing here but I think we can do it. :) @Emilgardis -- are you up for trying to go the extra mile here?

I think the error message we want is probably something like this:

error[E0593]: type mismatch in closure arguments
 --> src/main.rs:4:30
  |
4 |     let b: Vec<_> = a.iter().map(|x: (u32, u32)| 45).collect(); 
  |                              ^^^ ------------------ takes a `(u32, u32)`
  |                              |
  |                              expected closure that takes a `&(u32, u32)`

@@ -684,24 +684,36 @@ 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 {
let expected_len = if let ty::TyTuple(tys, _) = expected_found.expected.sty {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huh. I am a bit dubious about this if in the first-place. I guess I have to run the code -- what happens if you just remove this logic altogether, though? It feels very odd to me to have to do all this matching.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(In other words, the logic here feels quite suspect, even before your changes.)

@Emilgardis
Copy link
Contributor Author

I agree with what has been commented here. I'll get to the implementation most likely tomorrow. Should I open a new pr for it or just force push a new commit?

@Mark-Simulacrum
Copy link
Member

Force push is probably fine in this case since changes are small; in general just a normal push would be best

@Emilgardis Emilgardis changed the title Fixes #42143 Fix wrong report on closure args mismatch when a ref is expected but not found Jun 5, 2017
@nikomatsakis
Copy link
Contributor

@Emilgardis force push is good -- thanks!

@carols10cents
Copy link
Member

Hi @Emilgardis! Just a friendly ping to keep this on your radar :)

@nikomatsakis
Copy link
Contributor

@Emilgardis do you think you know how to do the better error message, or do you want a few tips?

@Emilgardis
Copy link
Contributor Author

Emilgardis commented Jun 17, 2017

Sorry for the delay, I have a unpushed commit on my local branch, I've removed the match @nikomatsakis suspected to be uneeded. So far no bugs regarding that change. EDIT:: this change removed E0593 completely.

I've also implemented E0604, and I'm now trying to get a correct output of the types that where mismatched. i.e show &(u32, u32) instead of std::ops::FnOnce<(&(u32, u32),)> (the latter is Display from a ty::Binder<ty::sty::TraitRef<'tcx>>

@Emilgardis
Copy link
Contributor Author

Emilgardis commented Jun 18, 2017

I realised that the error:

[E0604]: type mismatch in closure arguments
 --> src/main.rs:4:30
  |
4 |     let b: Vec<_> = a.iter().map(|x: (u32, u32)| 45).collect(); 
  |                              ^^^ ------------------ takes a `(u32, u32)`
  |                              |
  |                              expected closure that takes a `&(u32, u32)`

Is awfully closely related to E0281, should I remove my local E0604 and just change E0281 to give prettier output and have it be called on code like the example above instead of E0604?

281 is currently

fn foo<F>(_m: F) where F: Fn(usize) {}
----------
error[E0281]: type mismatch: `[closure@src/lib.rs:11:13: 11:26]` implements the trait `core::ops::FnOnce<(isize,)>`, but the trait `core::ops::FnOnce<(usize,)>` is required
  --> src/lib.rs:11:9
   |
11 |         foo(|y: isize| ());
   |         ^^^ ------------- implements `core::ops::FnOnce<(isize,)>`
   |         |
   |         requires `core::ops::FnOnce<(usize,)>`
   |         expected usize, found isize

@arielb1
Copy link
Contributor

arielb1 commented Jun 20, 2017

Do you need any help @Emilgardis? Feel free to ask me on IRC (I'm arielb1 or arielby).

@nikomatsakis
Copy link
Contributor

@Emilgardis it's always a tough call where to use a distinct error number. I think in this case it is worth keeping a distinct error number, though. Basically this is because we can give a more specialized --explain description targeting the particular case of closure arguments. Also, there is a sort of distinction here: the more general mismatched types refers to the value itself, whereas this is saying that the types in the trait that the closure implements don't match.

(Also, the current infrastructure makes it difficult to combine error messages in any case.)

@nikomatsakis
Copy link
Contributor

@Emilgardis sounds like you have something working? I guess you didn't yet push those commits? The preview looks very appealing though!

@arielb1
Copy link
Contributor

arielb1 commented Jun 27, 2017

@Emilgardis I couldn't find you on IRC. Are you still interested in working on this? Should I get this done myself?

@Emilgardis
Copy link
Contributor Author

Emilgardis commented Jun 28, 2017

Sorry for not being active, I'll get to this tomorrow (in 7-10 hours). I'm Emilgardis on mozilla

@aidanhs
Copy link
Member

aidanhs commented Jul 5, 2017

Hi @Emilgardis, did you get a chance to come back to this?

@Emilgardis
Copy link
Contributor Author

Emilgardis commented Jul 6, 2017

Yes, I have a working branch on temp-issue-42143. I'll get to rebasing it onto changes upstream today regarding a new error code

@nikomatsakis
Copy link
Contributor

@Emilgardis awesome :)

when a ref is expected but not found.

Adds new error E0622

Fixes rust-lang#42143
@Emilgardis
Copy link
Contributor Author

Emilgardis commented Jul 7, 2017

There we go. Should be all fine.

I'll address some concerns I have in a review.

--> $DIR/closure-arg-type-mismatch.rs:16:27
|
16 | let d1 = a.iter().map(|x: &(u16,u16)| 45);
| ^^^ ------------------ takes a `for<'r> std::ops::FnMut<(&'r (u16, u16),)>`
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this binder/lifetime be displayed?

15 | let b = a.iter().map(|x: (u32, u32)| 45);
| ^^^ ------------------ takes a `std::ops::FnMut<((u32, u32),)>`
| |
| expected closure that takes a `std::ops::FnMut<(&(u32, u32),)>`
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking about implementing a hint telling the user to add a &/&mut to the closure

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, I couldn't find a way (except for "parsing") to convert Fn*<([&]({T} ... ))> (meaning what is displayed here as FnMut<(&(u32, u32),)>) into a simple &(u32, u32) as @nikomatsakis suggested.

@Emilgardis Emilgardis force-pushed the issue-42143 branch 2 times, most recently from f2b7256 to a41f93f Compare July 7, 2017 03:17
New error code is E0623
Also cleanup/fix ui test
@Emilgardis
Copy link
Contributor Author

I'm not sure how to proceed. E0281 is very similar to E0623. Infact, what I can see the two errors are almost always the same.

let mut err = struct_span_err!(self.tcx.sess, span, E0623,
"type mismatch in closure arguments");
if let Some(sp) = found_span {
err.span_label(span, format!("expected closure that takes a `{}`", found));
Copy link
Contributor

@nikomatsakis nikomatsakis Jul 7, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should be able to extract the types of the arguments by examining the substs of the expected_ref and found trait-refs. Something like:

let expected_args = expected_ref.map_bound_ref(|trait_ref| trait_ref.substs.type_at(1));

But it might need to be type_at(0). That will give you a Binder<Ty<'tcx>>, which you should be able to use with {} I think.

Copy link
Contributor

@nikomatsakis nikomatsakis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is looking good! Last step is just to extract the types, and not print the whole trait, left some comments, let me know how it goes (feel free to reach out on IRC etc)

@nikomatsakis
Copy link
Contributor

@Emilgardis ping?

@arielb1
Copy link
Contributor

arielb1 commented Jul 18, 2017

@Emilgardis ping. I'll be on vacation for the rest of the week, but if you need any help you can talk with @nikomatsakis .

@arielb1
Copy link
Contributor

arielb1 commented Jul 25, 2017

Closing due to inactivity. Feel free to reopen when you can find me.

@arielb1 arielb1 closed this Jul 25, 2017
bors added a commit that referenced this pull request Sep 26, 2017
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants