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

Clippy suggests optimizing volatile operations away; Shoudln't do so? #3043

Closed
andre-richter opened this issue Aug 12, 2018 · 11 comments · Fixed by #4035
Closed

Clippy suggests optimizing volatile operations away; Shoudln't do so? #3043

andre-richter opened this issue Aug 12, 2018 · 11 comments · Fixed by #4035
Labels
C-bug Category: Clippy is not doing the correct thing good-first-issue These issues are a good way to get started with Clippy

Comments

@andre-richter
Copy link
Member

Hi,

given the following code sample, we read from an MMIO timer that has high and low parts.
If a wrap in low occurred soon after we read it, read the timers again.

extern crate volatile_register;

use volatile_register::RW;

#[repr(C)]
pub struct MMIO {
    pub high: RW<u32>,
    pub low: RW<u32>,
}

fn main() {
    let timer = 0x1337 as *const MMIO;

    unsafe {
        let mut hi = (*timer).high.read();
        let mut lo = (*timer).low.read();

        // Repeat if high word changed during read.
        if hi != (*timer).high.read() {
            hi = (*timer).high.read();
            lo = (*timer).low.read();
        }

        println!("{}", (u64::from(hi) << 32) | u64::from(lo));
    }
}

Clippy suggest alternative code in this case.

    Checking vcell v0.1.0                                                                           
    Checking volatile-register v0.2.0
    Checking volatile_clippy v0.1.0 (file:///home/andre/repos/volatile_clippy)
warning: `if _ { .. } else { .. }` is an expression
  --> src/main.rs:16:9
   |
16 | /         let mut lo = (*timer).low.read();
17 | |
18 | |         // Repeat if high word changed during read.
19 | |         if hi != (*timer).high.read() {
20 | |             hi = (*timer).high.read();
21 | |             lo = (*timer).low.read();
22 | |         }
   | |_________^ help: it is more idiomatic to write: `let <mut> lo = if hi != (*timer).high.read() { ..; (*timer).low.read() } else { (*timer).low.read() };`
   |
   = note: #[warn(useless_let_if_seq)] on by default
   = note: you might not need `mut` at all
   = help: for further information visit https://rust-lang-nursery.github.io/rust-clippy/v0.0.212/index.html#useless_let_if_seq

Using this suggestion would actually remove the first MMIO read of low. Since volatile instructs the compiler to not reorder volatile operations across each other and/or elide volatile operations, clippy shouldn't do so as well?

I understand this case might be difficult because the volatile hides in an extern crate.

@oli-obk
Copy link
Contributor

oli-obk commented Aug 13, 2018

We should probably disable this lint for values with interior mutability

@oli-obk oli-obk added C-bug Category: Clippy is not doing the correct thing good-first-issue These issues are a good way to get started with Clippy labels Aug 13, 2018
@ghost
Copy link

ghost commented Aug 14, 2018

How do you tell if a type has interior mutability?

@oli-obk
Copy link
Contributor

oli-obk commented Aug 14, 2018

there's an is_freeze function on Ty: https://doc.rust-lang.org/nightly/nightly-rustc/rustc/ty/struct.TyS.html#method.is_freeze

if false, the type has interior mutability. There might still be values like Option::<Cell<u32>>::None which have no interior mutability, but we can ignore that.

@JoshMcguigan
Copy link
Contributor

JoshMcguigan commented Sep 30, 2018

I tried to reproduce this with a RefCell in the playground, and this lint was not activated.

https://play.rust-lang.org/?gist=2667edcc6275927a006ac361a45e4448&version=nightly&mode=debug&edition=2015

@oli-obk Can you post a playground link illustrating what you were thinking?

@oli-obk
Copy link
Contributor

oli-obk commented Oct 1, 2018

@JoshMcguigan
Copy link
Contributor

I'll take a shot at this. Hopefully I'll have a PR ready in the next few days.

bors added a commit that referenced this issue May 2, 2019
useless_let_if_seq handle interior mutability

fixes #3043

This passes all tests, including a new one specifically dealing with a type with interior mutability. The main thing I'm unsure of is whether the span I used in the call to `is_freeze` is the most appropriate span to use, or if it matters.
@bors bors closed this as completed in #4035 May 2, 2019
@andre-richter
Copy link
Member Author

Hi @JoshMcguigan,

I just tested your code with https://github.com/rust-embedded/rust-raspi3-OS-tutorials/tree/master/09_delays using make clippy. You may need some tools installed as described in https://github.com/rust-embedded/rust-raspi3-OS-tutorials#prerequisites to make it work.
This code was the reason for me opening this issue originally. I still get the warning:

~/repos/rust-raspi3-OS-tutorials/09_delays [master*] » cargo clippy -V
clippy 0.0.212 (aeadf15 2019-09-03)
~/repos/rust-raspi3-OS-tutorials/09_delays [master*] » make clippy
cargo xclippy --target=aarch64-unknown-none
...
warning: `if _ { .. } else { .. }` is an expression
  --> src/delays.rs:69:9
   |
69 | /         let mut lo = self.SYSTMR_LO.get();
70 | |
71 | |         // We have to repeat if high word changed during read. This
72 | |         // will emit a clippy warning that needs be ignored, or you
...  |
76 | |             lo = self.SYSTMR_LO.get();
77 | |         }
   | |_________^ help: it is more idiomatic to write: `let <mut> lo = if hi != self.SYSTMR_HI.get() { ..; self.SYSTMR_LO.get() } else { self.SYSTMR_LO.get() };`
   |
   = note: `#[warn(clippy::useless_let_if_seq)]` on by default
   = note: you might not need `mut` at all
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#useless_let_if_seq

    Finished dev [unoptimized + debuginfo] target(s) in 1.01s

Could you have a look again? Please note that the voltaile nature of reading SYSTMR_ hides between extern crates and macros that might be difficult to follow at first.

@JoshMcguigan
Copy link
Contributor

JoshMcguigan commented Sep 19, 2019

Hi @andre-richter,

Sorry this wasn't able to resolve your issue. It seems we must have missed something when turning your example into a minimal example which could be included as a test in the clippy code base, but I don't know enough about the compiler internals to know what that would be.

If you can provide an example using only the standard library (no external libs) then we can add that to the clippy test suite to be sure it solves your problem.

Alternatively perhaps @oli-obk has some insight. Or maybe this issue should just be re-opened?

@andre-richter
Copy link
Member Author

andre-richter commented Sep 19, 2019

Hi @JoshMcguigan,

I think this playground should do.

Thanks for having a look 😊

@flip1995
Copy link
Member

Thanks for the minimal reproducer! Reopening the issue.

@flip1995 flip1995 reopened this Sep 20, 2019
bors added a commit that referenced this issue May 15, 2020
Downgrade useless_let_if_seq to nursery

I feel that this lint has the wrong balance of incorrect suggestions for a default-enabled lint.

The immediate code I faced was something like:

```rust
fn main() {
    let mut good = do1();
    if !do2() {
        good = false;
    }
    if good {
        println!("good");
    }
}

fn do1() -> bool { println!("1"); false }
fn do2() -> bool { println!("2"); false }
```

On this code Clippy calls it unidiomatic and suggests the following diff, which has different behavior in a way that I don't necessarily want.

```diff
- let mut good = do1();
- if !do2() {
-     good = false;
- }
+ let good = if !do2() {
+     false
+ } else {
+     do1()
+ };
```

On exploring issues filed about this lint, I have found that other users have also struggled with inappropriate suggestions (#4124, #3043, #2918, #2176) and suggestions that make the code worse (#3769, #2749). Overall I believe that this lint is still at nursery quality for now and should not be enabled.

---

changelog: Remove useless_let_if_seq from default set of enabled lints
bors added a commit that referenced this issue May 15, 2020
Downgrade useless_let_if_seq to nursery

I feel that this lint has the wrong balance of incorrect suggestions for a default-enabled lint.

The immediate code I faced was something like:

```rust
fn main() {
    let mut good = do1();
    if !do2() {
        good = false;
    }
    if good {
        println!("good");
    }
}

fn do1() -> bool { println!("1"); false }
fn do2() -> bool { println!("2"); false }
```

On this code Clippy calls it unidiomatic and suggests the following diff, which has different behavior in a way that I don't necessarily want.

```diff
- let mut good = do1();
- if !do2() {
-     good = false;
- }
+ let good = if !do2() {
+     false
+ } else {
+     do1()
+ };
```

On exploring issues filed about this lint, I have found that other users have also struggled with inappropriate suggestions (#4124, #3043, #2918, #2176) and suggestions that make the code worse (#3769, #2749). Overall I believe that this lint is still at nursery quality for now and should not be enabled.

---

changelog: Remove useless_let_if_seq from default set of enabled lints
@phansch
Copy link
Member

phansch commented Dec 21, 2020

Triage: The lint now is allow by default but this particular issue seems to have been fixed at some point: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=b60090a798f6723d1a08d44d2b58bc50

I'm going to go ahead and close this issue but feel free to re-open it, should the issue persist for you.

@phansch phansch closed this as completed Dec 21, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-bug Category: Clippy is not doing the correct thing good-first-issue These issues are a good way to get started with Clippy
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants