Skip to content

Detect double reference when applying binary op #34420

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

Closed
wants to merge 1 commit into from

Conversation

estebank
Copy link
Contributor

let vr = v.iter().filter(|x| {
    x % 2 == 0
});

will now yield the following compiler output:

ERROR binary operation `%` cannot be applied to type `&&_`
NOTE this is a reference of a reference to a type that `%` can be applied to,
you need to dereference this variable once for this operation to work
NOTE an implementation of `std::ops::Rem` might be missing for `&&_`

The first NOTE is new.

Bug #33877

@rust-highfive
Copy link
Contributor

Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @pnkfelix (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.

match lhs_ty.sty {
TypeVariants::TyRef(_, ref ty_mut) => {
match ty_mut.ty.sty {
TypeVariants::TyRef(_, ref ty_mut) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

if let? (Twice)

@hanna-kruppe
Copy link
Contributor

hanna-kruppe commented Jun 23, 2016

I like this note because I've seen plenty of people confused by this mistake. I have a little doubt about the wording though, the error message for something so confusing should be as simple as possible. Since the message already recommends deref'ing once, perhaps the "reference of a reference" part can be simplified? Something like:

NOTE the left-hand side is a reference, try dereferencing it to apply %

(This may be too short instead.)

@estebank
Copy link
Contributor Author

@rkruppe I incorporated the review comments, as well as added a new test for the &String + str case. Slightly reworded the NOTE, trying to have a middle point between the terseness of your proposed wording and the specificity of mine. Please let me know what you think.

err.span_note(
lhs_expr.span,
&format!(
"this is a reference of type that `{}` can be \
Copy link
Contributor

Choose a reason for hiding this comment

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

"reference of type that + can be applied to" sounds ungrammatical to me. I am not a native speaker, so I would like to invite others to chip in.

@hanna-kruppe
Copy link
Contributor

So I thought a bit more about &String + &str and it's actually nonsense for the note to trigger there. String + &str moves, so you can't just deref the &String. Sorry for the noise!

Perhaps you could add code to check if the lhs type is Copy? Then, I think, the suggestion should be pretty foolproof.

```rust
let vr = v.iter().filter(|x| {
    x % 2 == 0
});
```

will now yield the following compiler output:

```bash
ERROR binary operation `%` cannot be applied to type `&&_`
NOTE this is a reference of a reference to a type that `%` can be applied to,
you need to dereference this variable once for this operation to work
NOTE an implementation of `std::ops::Rem` might be missing for `&&_`
```

The first NOTE is new.

Bug rust-lang#33877
@estebank estebank force-pushed the double-reference branch from 961e371 to 3cdadcf Compare July 6, 2016 05:59
@estebank
Copy link
Contributor Author

@rkruppe, I haven't been able to retake this since I last pushed. I am not sure how to check wether the lefthand span TyS is Copy. How could I perform that check?

Beyond that, is there a way to determine wether the lhs is an argument to the current context, and if it is, getting the span for it? Having that I could expand the help text for those cases with something along the lines of:

You can dereference this variable in the argument list in this way:
  let vr = v.iter().filter(|ref x| {
                            ^^^^^

@hanna-kruppe
Copy link
Contributor

Checking whether a TyS is Copy might be done with moves_by_default, but I don't have a lot of experience with this sort of operation myself.

No idea how you might check for a parameter, but I wouldn't sweat it since &-pattern vs dereferencing is mostly a style issue.

cc @Manishearth do you know?

@Manishearth
Copy link
Member

I think moves_by_default is what you want

@pnkfelix
Copy link
Member

@estebank do you think you'll be able to incorporate the changes to check for Copy?

@estebank
Copy link
Contributor Author

@pnkfelix I've had some issues with the lifetimes associated to moves_by_default in order to check for Copy. I'll keep trying, but would welcome some help if someone from the team is available.

@alexcrichton
Copy link
Member

ping r? @pnkfelix

@brson
Copy link
Contributor

brson commented Nov 9, 2016

PR stalled. Waiting for updates from @pnkfelix or @estebank.

@pnkfelix
Copy link
Member

pnkfelix commented Dec 9, 2016

Sorry for the delay, I will try to assist.

@bors
Copy link
Collaborator

bors commented Dec 12, 2016

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

@pnkfelix
Copy link
Member

@estebank okay so while one could try to use moves_by_default, I think the better operation to use here is type_moves_by_default; these check_* methods are all on an FnCtxt which carries an infcx, and we can just supply the span of the LHS (lhs_expr.span).

I'll try to push a commit to your branch (a feature I've heard of but never used) that makes these changes (as well as the update to the &String + &str test, which should no longer emit the note as mentioned in the comments above), and then I'll let you rebase the PR atop master. Sound good?

@pnkfelix
Copy link
Member

Update: I was not able to push to the estebank:double-reference branch. Not sure why; maybe that checkbox is not checked off on the right hand side of this PR? Anyway, I'll fork this off on my own repo in the meantime.

In case @estebank would like to see, here is the key bit of the patch I added:

diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs
index 442fdb6..ca809a3 100644
--- a/src/librustc_typeck/check/op.rs
+++ b/src/librustc_typeck/check/op.rs
@@ -188,7 +188,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                             lhs_ty);
 
                         if let TypeVariants::TyRef(_, ref ty_mut) = lhs_ty.sty {
-                            if self.lookup_op_method(expr, ty_mut.ty, vec![rhs_ty_var],
+                            if !self.infcx.type_moves_by_default(ty_mut.ty, lhs_expr.span) &&
+                                self.lookup_op_method(expr, ty_mut.ty, vec![rhs_ty_var],
                                     token::intern(name), trait_def_id,
                                     lhs_expr).is_ok() {
                                 err.span_note(

@pnkfelix
Copy link
Member

pnkfelix commented Dec 26, 2016

A semi-related issue here is that there has been talk of extending the language so that references to copy-types just behave more like the underlying type itself in various ways; e.g. inserting auto-deref when passing &C to a spot that expects a C (and where C: Copy).

See e.g. https://internals.rust-lang.org/t/roadmap-2017-productivity-learning-curve-and-expressiveness/4097

But it may be a long time until that is proposed, gets through the RFC process, and is implemented. So I'll still plan to land this note in the meantime.

@pnkfelix
Copy link
Member

closing in favor of rebased version #38617. (Don't worry @estebank, I preserved your authorship! 😄)

@pnkfelix pnkfelix closed this Dec 26, 2016
steveklabnik added a commit to steveklabnik/rust that referenced this pull request Jan 4, 2017
Detect double reference when applying binary op

``` rust
let vr = v.iter().filter(|x| {
    x % 2 == 0
});
```

will now yield the following compiler output:

``` bash
ERROR binary operation `%` cannot be applied to type `&&_`
NOTE this is a reference of a reference to a type that `%` can be applied to,
you need to dereference this variable once for this operation to work
NOTE an implementation of `std::ops::Rem` might be missing for `&&_`
```

The first NOTE is new.

Fix rust-lang#33877

----

Thanks to @estebank for providing the original PR rust-lang#34420 (of which this is a tweaked rebase).
alexcrichton added a commit to alexcrichton/rust that referenced this pull request Jan 28, 2017
Detect double reference when applying binary op

``` rust
let vr = v.iter().filter(|x| {
    x % 2 == 0
});
```

will now yield the following compiler output:

``` bash
ERROR binary operation `%` cannot be applied to type `&&_`
NOTE this is a reference of a reference to a type that `%` can be applied to,
you need to dereference this variable once for this operation to work
NOTE an implementation of `std::ops::Rem` might be missing for `&&_`
```

The first NOTE is new.

Fix rust-lang#33877

----

Thanks to @estebank for providing the original PR rust-lang#34420 (of which this is a tweaked rebase).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants