-
Notifications
You must be signed in to change notification settings - Fork 13.1k
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
Fixed mutable vars being marked used when they weren't #43582
Conversation
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. |
You caught bugs in libcore! 😄 Also, you need to add three [00:17:02] error: variable does not need to be mutable
[00:17:02] --> /checkout/src/libcore/ops/function.rs:190:41
[00:17:02] |
[00:17:02] 190 | extern "rust-call" fn call_once(mut self, args: A) -> F::Output {
[00:17:02] | ^^^^^^^^
[00:17:02] |
[00:17:02] note: lint level defined here
[00:17:02] --> /checkout/src/libcore/lib.rs:68:9
[00:17:02] |
[00:17:02] 68 | #![deny(warnings)]
[00:17:02] | ^^^^^^^^
[00:17:02] = note: #[deny(unused_mut)] implied by #[deny(warnings)]
[00:17:02]
[00:17:03] error: variable does not need to be mutable
[00:17:03] --> /checkout/src/libcore/option.rs:875:18
[00:17:03] |
[00:17:03] 875 | fn into_iter(mut self) -> IterMut<'a, T> {
[00:17:03] | ^^^^^^^^
[00:17:03]
[00:17:03] error: variable does not need to be mutable
[00:17:03] --> /checkout/src/libcore/result.rs:912:18
[00:17:03] |
[00:17:03] 912 | fn into_iter(mut self) -> IterMut<'a, T> {
[00:17:03] | ^^^^^^^^
[00:17:03]
[00:17:03] error: aborting due to 3 previous errors
[00:17:03]
[00:17:03] error: Could not compile `core`. |
This would still be broken when you have a deref of a field: fn main() {
let mut x = 0;
let mut y = (&mut x,);
*y.0 = 1;
} You can detect upvar derefs (aka derefs of captured variables in closures) by looking for a That means you'll have to use the cmt instead of the loan path, but you have a cmt available. |
What three test cases are needed? The three issues are all instances of the same problem, or should I test for use of mutable references inside structs/tuples as well? @arielb1 I didn't notice that. The mut-checker can track upvars with |
@ivanbakel The test case for each issue reported. Perhaps modify #![allow(unused_assignments)]
#![allow(unused_variables)]
#![allow(dead_code)]
#![deny(unused_mut)]
fn main() {
...
// issue 43526
let mut x43526 = &mut 5; //~ ERROR: variable does not need to be mutable
*x43526 = 4;
// include 30280 here as well
}
// issue 25049, reduced
fn foo(mut buf: &mut [u8]) { //~ ERROR: variable does not need to be mutable
&mut buf[..];
}
... The example of arielb1's comment should be added as well if you fixed it. |
The code has been updated to catch derefs of borrows inside other types as well, which resolves the issue arielb1 raised - this has raised some new violations in core, so I'm waiting for those to be found and fixed as well. Is there something special about the order of operations for 25049 that makes it different from the other bugs? I'm not that familiar with slice logic. |
@ivanbakel Nothing special. The test cases are there to prove that you do have fixed all 3 issues, and to catch future regression. |
Alright, I've added tests for the 3 issue cases and the tuple case as well. |
Boxes are the exception - since a Box is as mutable as its contents.
The mutability system now checks where derefs go through borrows in the loan chain, and can correctly detect mutable borrows inside structs and tuples.
916e914
to
d3236a8
Compare
Now libcore and libstd test cases need to be updated 😂 [00:50:15] error: variable does not need to be mutable
[00:50:15] --> /checkout/src/libcore/../libcore/tests/slice.rs:108:9
[00:50:15] |
[00:50:15] 108 | let mut v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5];
[00:50:15] | ^^^^^
[00:50:15] |
[00:50:15]
[00:50:15] error: variable does not need to be mutable
[00:50:15] --> /checkout/src/libcore/../libcore/tests/slice.rs:112:9
[00:50:15] |
[00:50:15] 112 | let mut v2: &mut [i32] = &mut [0, 1, 2, 3, 4];
[00:50:15] | ^^^^^^
[00:50:15]
[00:50:15] error: variable does not need to be mutable
[00:50:15] --> /checkout/src/libcore/../libcore/tests/slice.rs:116:9
[00:50:15] |
[00:50:15] 116 | let mut v3: &mut [i32] = &mut [];
[00:50:15] | ^^^^^^
[00:50:15]
[00:50:15] error: variable does not need to be mutable
[00:50:15] --> /checkout/src/libcore/../libcore/tests/slice.rs:123:9
[00:50:15] |
[00:50:15] 123 | let mut v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5];
[00:50:15] | ^^^^^
[00:50:15]
[00:50:15] error: variable does not need to be mutable
[00:50:15] --> /checkout/src/libcore/../libcore/tests/slice.rs:128:9
[00:50:15] |
[00:50:15] 128 | let mut v2: &mut [i32] = &mut [0, 1, 2, 3, 4];
[00:50:15] | ^^^^^^
[00:50:15]
[00:50:15] error: variable does not need to be mutable
[00:50:15] --> /checkout/src/libcore/../libcore/tests/slice.rs:197:9
[00:50:15] |
[00:50:15] 197 | let mut v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5];
[00:50:15] | ^^^^^
[00:50:15]
[00:50:20] error: variable does not need to be mutable
[00:50:20] --> /checkout/src/libstd/error.rs:517:13
[00:50:20] |
[00:50:20] 517 | let mut a = &mut a as &mut (Error + 'static);
[00:50:20] | ^^^^^
[00:50:20] |
[00:50:20]
[00:50:20] error: variable does not need to be mutable
[00:50:20] --> /checkout/src/libstd/io/cursor.rs:459:13
[00:50:20] |
[00:50:20] 459 | let mut reader = &mut &in_buf[..];
[00:50:20] | ^^^^^^^^^^
[00:50:20] |
I finally understand the meaning of the words "technical debt". |
None | ||
} | ||
LpUpvar(ty::UpvarId{ var_id: local_id, closure_expr_id: _ }) => { | ||
self.tcx().used_mut_nodes.borrow_mut().insert(local_id); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No through_borrow
here?
fn main() {
let mut z = 0;
let mut x = &mut z;
let mut klos = || {
*x = 1;
};
klos();
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's not needed because closures don't borrow mutably (they borrow uniquely) unless they have to. But please add a test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe I'm don't understand your example: This already gives a warning for x
being mut
, even with the new code. through_borrow
is always true reaching the upvar case, since it is always wrapped in a LpExtend(.., .., LpDeref(..))
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure. I understand why it works. Just add it as a test to make sure it does work, because it could be an edge case (e.g. when we move to MIR borrowck, we'll like to keep everything working).
r=me with closure test (or is such a test already present?) |
Nice PR! |
Alright, I've added a test for the closure case. |
@bors r+ |
📌 Commit d1fffd8 has been approved by |
⌛ Testing commit d1fffd8 with merge e399d7ee6953f3960158ea009f2b93e9a86292b0... |
😢 |
I'll reboot to my Windows tonight and push a fix to your repo. |
@bors r+ |
📌 Commit 3b0ff0d has been approved by |
⌛ Testing commit 3b0ff0d with merge 4a8360c3a24d96daf26234ac15cc15b53584d975... |
💔 Test failed - status-travis |
Legit. It's
@ivanbakel You need to
Meanwhile you may want to check if RLS and other dependencies (if they are compiled with |
I'll get on making the Cargo PR. Is there an easy way to build all submodules to check for these problems? |
@ivanbakel I'd just do it manually 😂 Out of all submodules,
That leaves |
Fixed some variables being unnecessarily mutable ### Changes Some variables are marked `mut` when they don't need to be. This PR changes those variables to no longer be `mut`. ### Context PR on rust-lang/rust#43582 tl:dr; There's a bug with the mutability checker that sometimes marks mutable ref variables as being used when they're not.
After some work:
|
Maybe someone could just r+ and let bors figure it out (after the cargo submodule is updated, that is). |
@bors r+ |
📌 Commit 8f78d45 has been approved by |
Fixed mutable vars being marked used when they weren't #### NB : bootstrapping is slow on my machine, even with `keep-stage` - fixes for occurances in the current codebase are <s>in the pipeline</s> done. This PR is being put up for review of the fix of the issue. Fixes #43526, Fixes #30280, Fixes #25049 ### Issue Whenever the compiler detected a mutable deref being used mutably, it marked an associated value as being used mutably as well. In the case of derefencing local variables which were mutable references, this incorrectly marked the reference itself being used mutably, instead of its contents - with the consequence of making the following code emit no warnings ``` fn do_thing<T>(mut arg : &mut T) { ... // don't touch arg - just deref it to access the T } ``` ### Fix Make dereferences not be counted as a mutable use, but only when they're on borrows on local variables. #### Why not on things other than local variables? * Whenever you capture a variable in a closure, it gets turned into a hidden reference - when you use it in the closure, it gets dereferenced. If the closure uses the variable mutably, that is actually a mutable use of the thing being dereffed to, so it has to be counted. * If you deref a mutable `Box` to access the contents mutably, you are using the `Box` mutably - so it has to be counted.
☀️ Test successful - status-appveyor, status-travis |
`iter_mut()` and `or_insert()` yields mutable references, the variables they bind to does not need to be mutable. Previously, because of rust-lang/rust#30280 this was not considered a warning. The fix is in rust-lang/rust#43582 This means on recently nightlies this warning is emitted.
Compiling a bin project under 1.22.1 issues In other words, code gets compiled under 1.24 with or without the unused Trying to get a minimal snippet to reproduce this but the offending line is like this: {
let (x, mut y) = setup().unwrap_or_else(process::exit(1));
foo(x, &y);
bar(x, &y);
} Was just wondering if this PR is related to above issue? |
NB : bootstrapping is slow on my machine, even with
keep-stage
- fixes for occurances in the current codebase arein the pipelinedone. This PR is being put up for review of the fix of the issue.Fixes #43526, Fixes #30280, Fixes #25049
Issue
Whenever the compiler detected a mutable deref being used mutably, it marked an associated value as being used mutably as well. In the case of derefencing local variables which were mutable references, this incorrectly marked the reference itself being used mutably, instead of its contents - with the consequence of making the following code emit no warnings
Fix
Make dereferences not be counted as a mutable use, but only when they're on borrows on local variables.
Why not on things other than local variables?
Box
to access the contents mutably, you are using theBox
mutably - so it has to be counted.