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

Warn when a local is "mut" but clearly not mutated #3083

Closed
jruderman opened this issue Aug 1, 2012 · 9 comments
Closed

Warn when a local is "mut" but clearly not mutated #3083

jruderman opened this issue Aug 1, 2012 · 9 comments
Labels
A-lint Area: Lints (warnings about flaws in source code) such as unused_mut. C-enhancement Category: An issue proposing an enhancement or a PR with one. E-easy Call for participation: Easy difficulty. Experience needed to fix: Not much. Good first issue.

Comments

@jruderman
Copy link
Contributor

I think this should warn:

fn main()
{
    let mut x = 3;
    assert x == 3;
}
@bblum
Copy link
Contributor

bblum commented Aug 2, 2012

+1. i'm all for compiler warnings that make folks tighten down their code.

@bstrie
Copy link
Contributor

bstrie commented Aug 7, 2012

Would it work similarly to the unused variable detection, where it warns unless you stick an underscore on the front of the identifier?

@zmanji
Copy link

zmanji commented Aug 8, 2012

I would like to work on this issue but I don't know where to start. Can anyone provide me with some tips?

@nikomatsakis
Copy link
Contributor

The liveness pass already pretty much computes the information you need. It can be found in src/rustc/middle/liveness.rs, but you probably want to come on IRC and ask me (nmatsakis) questions about it.

@nikomatsakis
Copy link
Contributor

I'm not sure how familiar you are with a traditional liveness analysis, but the basic idea is that it tracks when variables may be used again (in which case they are live). Ours is a bit different in that it also tracks when variables may be reassigned in the future. Therefore, it should be possible to check, at the point where a variable is declared, whether it will ever be reassigned.

The easiest thing would be to issue a warning for mutable variables which have initializers but are never reassigned (such as the example given in this bug). Such a warning would be issued in check_local(). There is currently a check that says "if there is an initializer and the variable is immutable, ensure it is never assigned again". You could repurpose this to something like "if there is an initializer, and the variable is never reassigned again, warn if it is not immutable" (but of course it should still report the error it does today)

You might conceivably also try to issue warnings for variables that are assigned only once in the flow of execution:

let mut foo;
if cond { foo = 1; } else { foo = 2; }
...

as such variables also do not require a mut label. But this is trickier, I'm not sure if we have enough information available to distinguish that case from one like;

let mut foo;
foo = 3;
...
if cond { foo = 1; } else { foo = 2; }
...

That is, each assignment can determine whether there are ever any later assignments and whether the variable is ever used later. But here it seems like we would also need to know whether there have been any prior assignments. So I'd say just warn if there is an initializer and ignore this second case.

@nikomatsakis
Copy link
Contributor

Note: Be sure to add a "compile-fail" test case as described here: https://github.com/mozilla/rust/wiki/Note-testsuite

This one is a bit tricky as you'll be detecting a warning, and the test suite likes for such tests to fail, so you'll probably have to add a secondary error. You can check out the test src/test/liveness-unused.rs as an example.

@ttaubert
Copy link
Contributor

It's unfortunately not as easy as just adding another check to check_local(). Variables might need to be mutable not only for re-assignment:

let tmps = ~[];
for vec::each(args) |arg| {
    tmps.push(copy *arg);
}
let s = ~"foobar";
let head = str::shift_char(&mut s);

These both result in error: illegal borrow: creating mutable alias to immutable local variable.

@huonw
Copy link
Member

huonw commented Dec 16, 2012

I have little idea of the infrastructure (I had a look at middle/liveness.rs, which didn't help me much), but how difficult would it be to add an used_mutably_count to the analysis that counts the maximum number of times a variable could possibly be assigned or used in a context that requires mut (accounting for loops)?

e.g.

let mut a;
a = 1;
// used_mutably_count(a) == Known(1)

let mut b = 2;
b = 1;
// used_mutably_count(b) == Known(2)

let mut c = ~[];
for vec::each(args) |arg| {
    c.push(copy *args); 
}
// used_mutably_count(c) == Unknown

Then if used_mutably_count(var) == Known(1), rustc can warn that var should be immutable (or not, if var is already immutable).

It could be computed with rules like

umc(var, if cond { blk1 } else { blk2 }) = max(umc(var, blk1), umc(var, blk2))
umc(var, { stmt1; stmt2; ... }) = umc(var, stmt1) + umc(var, stmt2) + ...
umc(var, while expr { body }) = if umc(var, body) > Known(0) || umc(var, expr) > Known(0) { Unknown } else { Known(0) }
umc(var, let var = ...;) = Known(1)
umc(var, let var;) = Known(0)

where umc(var, ast) counts the maximum possibly assignments & mutable-uses of var in ast, the operations are such that the result is Unknown if any of the inputs are Unknown and Unknown > Known(k) for all k.

(I guess the count could even be compressed to UsedNever, UsedOnce and UsedMany.)

@graydon
Copy link
Contributor

graydon commented Mar 20, 2013

non-critical for 0.6, de-milestoning

@bors bors closed this as completed in aba93c6 Apr 27, 2013
bors pushed a commit to rust-lang-ci/rust that referenced this issue May 15, 2021
RalfJung pushed a commit to RalfJung/rust that referenced this issue Sep 28, 2023
GC the Stacked Borrows allocation history

This handles the biggest contributor to rust-lang/miri#3080

The benchmark that this adds demonstrates the memory improvement here, but our benchmark setup doesn't record memory usage, and `hyperfine` doesn't support emitting memory usage stats. I ran this benchmark manually with `/usr/bin/time -v cargo +miri miri run` 🤷
celinval pushed a commit to celinval/rust-dev that referenced this issue Jun 4, 2024
Instead of turning on the address sanitizer to ensure storage markers
(`StorageLive` and `StorageDead`) are kept in MIR, directly disable the
MIR pass that removes them from MIR
(https://rust-lang.zulipchat.com/#narrow/stream/182449-t-compiler.2Fhelp/topic/.E2.9C.94.20Keeping.20MIR's.20storage.20markers).
This is to ensure we don't get additional instrumentation that is not
relevant for Kani, and may unnecessarily increase the code size and
compilation time.

By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 and MIT licenses.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-lint Area: Lints (warnings about flaws in source code) such as unused_mut. C-enhancement Category: An issue proposing an enhancement or a PR with one. E-easy Call for participation: Easy difficulty. Experience needed to fix: Not much. Good first issue.
Projects
None yet
Development

No branches or pull requests

8 participants