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

Start of implementation of proposal for E0308 #37388

Closed

Conversation

GuillaumeGomez
Copy link
Member

if let Some(impl_infos) = self.tcx.inherent_impls.borrow().get(&found) {
let mut methods = Vec::new();
for impl_ in impl_infos {
methods.append(&mut self.tcx
Copy link
Contributor

Choose a reason for hiding this comment

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

use .extend and remove the .collect()

Copy link
Contributor

@KalitaAlexey KalitaAlexey Oct 25, 2016

Choose a reason for hiding this comment

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

I am sure that this can be replaced with flat_map, isn't it? This will reduce allocations, I think. Or just with nested for loop.

@@ -527,7 +527,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
{
let expected_found = match values {
None => None,
Some(values) => match self.values_str(&values) {
Some(ref values) => match self.values_str(&values) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Could somebody explain me why ref is added?

Copy link
Member Author

Choose a reason for hiding this comment

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

To avoid moving values says rustc.

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh. I understood. We can write self.values_str(values) because values is reference or am I wrong?

Copy link
Member Author

Choose a reason for hiding this comment

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

No, I think you're right. I didn't focus on this point when I wrote it. Thanks! :)

Copy link
Contributor

Choose a reason for hiding this comment

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

@GuillaumeGomez Will you fix this before merge?

Copy link
Member Author

Choose a reason for hiding this comment

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

Of course I will. This is far from merge anyway. I opened in order to allow @jonathandturner to follow.

@nikomatsakis
Copy link
Contributor

cc me

@@ -565,19 +565,29 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
// look for expected with found id
self.tcx.populate_inherent_implementations_for_type_if_necessary(found);
if let Some(impl_infos) = self.tcx.inherent_impls.borrow().get(&found) {
let mut methods = Vec::new();
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you write like this:

let methods = impl_infos.iter()
                        .flat_map(|impl_| {
                          self.tcx
                              .impl_or_trait_items(*impl_)
                              .iter()
                              .map(|&did| (None, did, self.tcx.impl_or_trait_item(did)))
                              .filter(|&(_, _, ref x)| {
                                self.matches_return_type(x, &expected_ty)
                              })
                        });

I didn't test it, so it may be impossible.

self.matches_return_type(item, &item_ty)
})
}
};
Copy link
Member

Choose a reason for hiding this comment

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

You should've replaced the original code to a call to impl_or_trait_item - since they're equivalent ;).

@GuillaumeGomez
Copy link
Member Author

We now have for the following code:

fn main() {
    let x: usize = String::new();
}

the following error output:

error[E0308]: mismatched types
 --> t.rs:2:20
  |
2 |     let x: usize = String::new();
  |                    ^^^^^^^^^^^^^ expected usize, found struct `std::string::String`
  |
  = note: expected type `usize`
  = note:    found type `std::string::String`
  = help: no safe suggestion found, here are functions which match your needs but be careful:
 - capacity
 - len

error: aborting due to previous error

A few things remain to be done but this is a good start:

  • I created the safe_suggestion attribute.
  • I filter potential methods based on some precise parameters (others could be added but like I said, this is a start).
  • The DiagnosticBuilder is now returned in order to be able to add the suggestions into the help.

What remains to be done:

  • & propositions (String -> &str for example).
  • & vs &mut.
  • Add safe_suggestion attributes on "conversion" methods (the list is quite long...).
  • Maybe add more filters on the suggestions we have.

@eddyb
Copy link
Member

eddyb commented Oct 26, 2016

I don't like safe_suggestion - I'd rather we do it through (relatively simple) heuristics rather than annotating half of the libraries.

Also, let x: T = y: S; could suggest <T>::method if it's S -> T.
That would require a "search by first argument" mode, too.

@@ -1234,6 +1234,7 @@ impl String {
/// assert_eq!(a.len(), 3);
/// ```
#[inline]
#[cfg_attr(not(stage0), safe_suggestion)]
Copy link
Member Author

Choose a reason for hiding this comment

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

Which reminds me that this line should be removed.

@bors
Copy link
Contributor

bors commented Oct 27, 2016

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

@GuillaumeGomez GuillaumeGomez force-pushed the cast_suggestions branch 4 times, most recently from d261e3f to 7f53f51 Compare October 29, 2016 22:09
@GuillaumeGomez GuillaumeGomez changed the title [DON'T MERGE] Start of implementation of proposal for E0308 Start of implementation of proposal for E0308 Oct 29, 2016
@sophiajt
Copy link
Contributor

Can you post some examples of what the output currently looks like for the new errors?

@GuillaumeGomez
Copy link
Member Author

Sure. So for the following file:

fn test(_x: &mut String) {}
fn test2(_x: &mut i32) {}

fn main() {
    let x: usize = String::new();
    let x: &str = String::new();
    let y = String::new();
    test(&y);
    test2(&y);
}

It prints:

screen shot 2016-10-30 at 00 32 27

@sophiajt
Copy link
Contributor

Cool :) Looks good so far.

@sophiajt
Copy link
Contributor

There's a span_suggestion if you wanted to play with that.

.map(|probe| probe.to_unadjusted_pick())
.collect();

if ret.len() < 1 {
Copy link
Contributor

Choose a reason for hiding this comment

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

Now that PickResult is already a Result<Vec<->, ->, I don't think we need that Option.

Copy link
Member Author

Choose a reason for hiding this comment

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

You're absolutely right!

if suggestions.len() > 0 {
Some(format!("here are some functions which \
might fulfill your needs:\n - {}",
self.get_best_match(&suggestions)))
Copy link
Contributor

Choose a reason for hiding this comment

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

I was wondering if we should call these methods and show them like:

  • .len()
  • .capacity()

Copy link
Member Author

Choose a reason for hiding this comment

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

Seems actually a better idea than the current output so big 👍 .

@@ -0,0 +1,47 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we use ui tests instead so we know what the result will look like?

Copy link
Member Author

Choose a reason for hiding this comment

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

Sure.

@GuillaumeGomez GuillaumeGomez force-pushed the cast_suggestions branch 2 times, most recently from 8087837 to d311d6c Compare November 1, 2016 15:02
@GuillaumeGomez
Copy link
Member Author

@jonathandturner: I moved the new test into ui, let me know if you like the new output.

r? @nikomatsakis

@nikomatsakis
Copy link
Contributor

@GuillaumeGomez

Sorry for the delay, been a busy last few days! I'll try to finish up the review now. I just wanted to revisit the method check code one last time with a fresh pair of eyes. =)

BTW, you wrote this earlier:

This is quite limited and would only work for rustc source code and not users' code in the majority of cases.

But I'm not sure what you were responding to? Was it this comment of mine:

I do wonder if we could leverage the as_, to_, and into_ conversions here, which basically signal "safe suggestions" already.

If so, I'm surprised that you think this would only be applicable to rustc! I think maybe what you mean is that it would be helpful for making suggestions from the standard library, but not for detecting cases of safe-conversions that occur in user code? If so, I am not sure I agree, though I suppose that may be possible. Presuming that end-users follow Rust's naming conventions, relying on as etc would seem to have the advantage that existing libraries would "just work" with this suggestion scheme.

@nikomatsakis
Copy link
Contributor

@GuillaumeGomez ok, so I've been spending some time re-reading the PR, and I feel like I'm not ready for it to land. I think we want to change the approach somewhat. There are two main concerns.

Concern 1: This makes some reasonable deep changes to the method selection code. In general, I don't think we should be changing the method lookup code quite so much to accommodate selections.

I think a better approach would be to factor out into two phases:

  1. First, we identify the list of candidates with a suitable return type, but without doing the full probe etc. We just get back a list of steps and candidates at each step.
  2. Once we have that list of candidates, we extract out a list of method names.
  3. Then we run the normal method lookup completely unaltered for each method name in turn. We ignore errors and just collect the Ok results to use as suggestions.

I expect this would be more of a "surface edit" to the method lookup code, basically, only tweaking how we generate candidates.

Does that make sense? If not, I can try to prototype to show you what I mean.

Concern 2: I remain uncomfortable with the code that suggests the user add a & or &mut. I tested it locally and it definitely misfires. I think the general rule we shoot for is "first, do no harm" -- meaning, don't give misleading suggestions. Telling people to unilaterally add an &mut doesn't feel helpful to me. This code might be ok if it (at minimum) checked that the type which resulted could possibly be right, but it doesn't right now.

Here is an example interaction with your branch showing you what I mean (though it's the same example I gave before):

lunch-box. cat > ~/tmp/issue-37388.rs
fn foo(x: &i32) {
    bar(x); // don't want a hint here!
}
fn bar(x: &mut i32) {}
fn main() { }

lunch-box. rustc --stage1 ~/tmp/issue-37388.rs
error[E0308]: mismatched types
 --> /home/nmatsakis/tmp/issue-37388.rs:2:9
  |
2 |     bar(x); // don't want a hint here!
  |         ^ types differ in mutability
  |
  = note: expected type `&mut i32`
  = note:    found type `&i32`
  = help: try with `&mut x`

error: aborting due to previous error

The other concern is that the final help is kind of hard to even see!

In general, I'd prefer to factor out this "suggest a &" code into a separate PR, since it seems like a different thing.

@GuillaumeGomez
Copy link
Member Author

GuillaumeGomez commented Nov 8, 2016

@nikomatsakis: For your second concern, I think I'll just make this change in another PR, it'll get easier for everyone and I'll try to make something better. However, the only real problem with the current code is for the &mut proposition in bad cases, no? Did you find a case where I proposed a & where I shouldn't?

For your first concern, well, I'm a bit lost.

  1. First, we identify the list of candidates with a suitable return type, but without doing the full probe etc. We just get back a list of steps and candidates at each step.

As far as I understood it, you said: "finding candidates without looking for candidates". So I certainly completely misunderstood and I really need more information. Also, a step actually correspond to only one candidate. Such a thing doesn't seem suitable for this case, right? Or maybe do you prefer to return multiple steps at once?

  1. Once we have that list of candidates, we extract out a list of method names.

Well, since we have methods' information, I guess this part doesn't need much comment and comprehension. 😉

  1. Then we run the normal method lookup completely unaltered for each method name in turn. We ignore errors and just collect the Ok results to use as suggestions.

Now I'm completely lost. Why would we need to run another method lookup since we already have a list of candidates?

So I think most of my questions come from my lack of knowledge but if you could bring a little more information, it'd wonderful!

Meanwhile, I'll remove the check_ref code from here and open another PR and try to work out the &mut issue.

@GuillaumeGomez GuillaumeGomez mentioned this pull request Nov 8, 2016
@bors
Copy link
Contributor

bors commented Nov 9, 2016

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

@alexcrichton alexcrichton added the T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. label Nov 10, 2016
@nikomatsakis
Copy link
Contributor

@GuillaumeGomez sorry for dropping off the radar last week. I've not forgotten you =) will try to get you my idea tomorrow.

@GuillaumeGomez
Copy link
Member Author

Ok! :)

We now do two phases. First, we gather up the list of candidates with
suitable return types and extract their names. Then we filter those to
see which are applicable and we return that.

It might be nice to do the "filter by return type" as a second step,
but this is ok for now.
@nikomatsakis
Copy link
Contributor

@GuillaumeGomez see my most recent commits for my proposed approach. The key point is that we do not change any code in the 'non-error' path (or at least, that's the goal).

@nikomatsakis
Copy link
Contributor

@GuillaumeGomez (I pushed the commits to your branch)

@sophiajt
Copy link
Contributor

@nikomatsakis - do you have a screenshot showing what your version looks like?

@nikomatsakis
Copy link
Contributor

@jonathandturner I did not change the appearance of the error messages at all, just the way they are gathered up. I tested it on the one or two UI tests and it seemed to generate the same output. I also agree we can improve the formatting though!

@GuillaumeGomez
Copy link
Member Author

@nikomatsakis: Really nice! This is way more elegant like this. I'm all for just keeping your code and merge it as is (in condition that it succeeds tests). Do you want to rebase or do I?

@nikomatsakis
Copy link
Contributor

@GuillaumeGomez ok. Perhaps you can rebase, and also remove the code that overlaps with #37658 ?

@GuillaumeGomez
Copy link
Member Author

@nikomatsakis: Sure. Give me a bit of time to take care of it. :)

@nikomatsakis
Copy link
Contributor

@GuillaumeGomez ok -- I think I'll close this PR for now to clear my queue, feel free to re-open. =)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants