Skip to content

Commit

Permalink
Auto merge of #6162 - josephlr:empty-loop-no-std, r=flip1995
Browse files Browse the repository at this point in the history
Enable empty_loop for no_std crates.

Fixes #6161

This change:
  - Makes it so the `empty_loop` lint will fire for `no_std` crates, but with a `no_std`-specific message (mentioning [`spin_loop_hint`](https://doc.rust-lang.org/core/sync/atomic/fn..html)).
  - Updates the `std` message to also mention [`std::thread::yield_now`](https://doc.rust-lang.org/std/thread/fn.yield_now.html).
  - Updates the docs to also mention the [LLVM infinite loop bug](rust-lang/rust#28728)
  - Updates the tests/stderr files to test this change.

changelog: Update `empty_loop` lint to work with `no_std` crates
  • Loading branch information
bors committed Oct 17, 2020
2 parents 4e83a38 + 012413d commit 53a59e5
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 21 deletions.
2 changes: 1 addition & 1 deletion clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1570,7 +1570,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY),
LintId::of(&len_zero::LEN_ZERO),
LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING),
LintId::of(&loops::EMPTY_LOOP),
LintId::of(&loops::FOR_KV_MAP),
LintId::of(&loops::NEEDLESS_RANGE_LOOP),
LintId::of(&loops::SAME_ITEM_PUSH),
Expand Down Expand Up @@ -1757,6 +1756,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&inline_fn_without_body::INLINE_FN_WITHOUT_BODY),
LintId::of(&let_underscore::LET_UNDERSCORE_LOCK),
LintId::of(&literal_representation::MISTYPED_LITERAL_SUFFIXES),
LintId::of(&loops::EMPTY_LOOP),
LintId::of(&loops::FOR_LOOPS_OVER_FALLIBLES),
LintId::of(&loops::ITER_NEXT_LOOP),
LintId::of(&loops::NEVER_LOOP),
Expand Down
65 changes: 52 additions & 13 deletions clippy_lints/src/loops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,18 +293,55 @@ declare_clippy_lint! {
declare_clippy_lint! {
/// **What it does:** Checks for empty `loop` expressions.
///
/// **Why is this bad?** Those busy loops burn CPU cycles without doing
/// anything. Think of the environment and either block on something or at least
/// make the thread sleep for some microseconds.
/// **Why is this bad?** Due to [an LLVM codegen bug](https://github.com/rust-lang/rust/issues/28728)
/// these expressions can be miscompiled or 'optimized' out. Also, these busy loops
/// burn CPU cycles without doing anything.
///
/// **Known problems:** None.
/// It is _almost always_ a better idea to `panic!` than to have a busy loop.
///
/// If panicking isn't possible, think of the environment and either:
/// - block on something
/// - sleep the thread for some microseconds
/// - yield or pause the thread
///
/// For `std` targets, this can be done with
/// [`std::thread::sleep`](https://doc.rust-lang.org/std/thread/fn.sleep.html)
/// or [`std::thread::yield_now`](https://doc.rust-lang.org/std/thread/fn.yield_now.html).
///
/// For `no_std` targets, doing this is more complicated, especially because
/// `#[panic_handler]`s can't panic. To stop/pause the thread, you will
/// probably need to invoke some target-specific intrinsic. Examples include:
/// - [`x86_64::instructions::hlt`](https://docs.rs/x86_64/0.12.2/x86_64/instructions/fn.hlt.html)
/// - [`cortex_m::asm::wfi`](https://docs.rs/cortex-m/0.6.3/cortex_m/asm/fn.wfi.html)
///
/// If you just care about fixing the LLVM bug (and don't care about burning
/// CPU), you can:
/// - On nightly, insert a non-`pure` `asm!` statement. For example:
/// ```rust
/// loop {
/// unsafe { asm!("") };
/// }
/// ```
/// Alternatively, you can compile your code with `-Zinsert-sideeffect`,
/// which will prevent the LLVM from miscompiling your code.
/// - On stable, insert a [`read_volatile`](https://doc.rust-lang.org/core/ptr/fn.read_volatile.html)
/// operation in the loop body. For example:
/// ```rust
/// let dummy = 0u8;
/// loop {
/// unsafe { core::ptr::read_volatile(&dummy) };
/// }
/// ```
///
/// **Known problems:** This is a correctness lint due to the LLVM codegen
/// bug. Once the bug is fixed, this will go back to being a style lint.
///
/// **Example:**
/// ```no_run
/// loop {}
/// ```
pub EMPTY_LOOP,
style,
correctness,
"empty `loop {}`, which should block or sleep"
}

Expand Down Expand Up @@ -502,14 +539,16 @@ impl<'tcx> LateLintPass<'tcx> for Loops {
// (even if the "match" or "if let" is used for declaration)
if let ExprKind::Loop(ref block, _, LoopSource::Loop) = expr.kind {
// also check for empty `loop {}` statements
if block.stmts.is_empty() && block.expr.is_none() && !is_no_std_crate(cx.tcx.hir().krate()) {
span_lint(
cx,
EMPTY_LOOP,
expr.span,
"empty `loop {}` detected. You may want to either use `panic!()` or add \
`std::thread::sleep(..);` to the loop body.",
);
if block.stmts.is_empty() && block.expr.is_none() {
let msg = "empty `loop {}` is currently miscompiled and wastes CPU cycles";
let help = if is_no_std_crate(cx.tcx.hir().krate()) {
"You should either use `panic!()` or add some call \
to sleep or pause the thread to the loop body."
} else {
"You should either use `panic!()` or add \
`std::thread::sleep(..);` to the loop body."
};
span_lint_and_help(cx, EMPTY_LOOP, expr.span, msg, None, help);
}

// extract the expression from the first statement (if any) in a block
Expand Down
2 changes: 1 addition & 1 deletion src/lintlist/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,7 @@ vec![
},
Lint {
name: "empty_loop",
group: "style",
group: "correctness",
desc: "empty `loop {}`, which should block or sleep",
deprecation: None,
module: "loops",
Expand Down
5 changes: 3 additions & 2 deletions tests/ui/crashes/ice-360.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@ LL | | }
|
= note: `-D clippy::while-let-loop` implied by `-D warnings`

error: empty `loop {}` detected. You may want to either use `panic!()` or add `std::thread::sleep(..);` to the loop body.
error: empty `loop {}` is currently miscompiled and wastes CPU cycles
--> $DIR/ice-360.rs:10:9
|
LL | loop {}
| ^^^^^^^
|
= note: `-D clippy::empty-loop` implied by `-D warnings`
= note: `#[deny(clippy::empty_loop)]` on by default
= help: You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body.

error: aborting due to 2 previous errors

11 changes: 8 additions & 3 deletions tests/ui/empty_loop.stderr
Original file line number Diff line number Diff line change
@@ -1,22 +1,27 @@
error: empty `loop {}` detected. You may want to either use `panic!()` or add `std::thread::sleep(..);` to the loop body.
error: empty `loop {}` is currently miscompiled and wastes CPU cycles
--> $DIR/empty_loop.rs:9:5
|
LL | loop {}
| ^^^^^^^
|
= note: `-D clippy::empty-loop` implied by `-D warnings`
= help: You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body.

error: empty `loop {}` detected. You may want to either use `panic!()` or add `std::thread::sleep(..);` to the loop body.
error: empty `loop {}` is currently miscompiled and wastes CPU cycles
--> $DIR/empty_loop.rs:11:9
|
LL | loop {}
| ^^^^^^^
|
= help: You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body.

error: empty `loop {}` detected. You may want to either use `panic!()` or add `std::thread::sleep(..);` to the loop body.
error: empty `loop {}` is currently miscompiled and wastes CPU cycles
--> $DIR/empty_loop.rs:15:9
|
LL | 'inner: loop {}
| ^^^^^^^^^^^^^^^
|
= help: You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body.

error: aborting due to 3 previous errors

7 changes: 6 additions & 1 deletion tests/ui/issue-3746.rs → tests/ui/empty_loop_no_std.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,16 @@ use core::panic::PanicInfo;

#[start]
fn main(argc: isize, argv: *const *const u8) -> isize {
loop {}
// This loop should not cause a warning.
let dummy = 0u8;
loop {
unsafe { core::ptr::read_volatile(&dummy) };
}
}

#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
// But this loop will cause a warning.
loop {}
}

Expand Down
11 changes: 11 additions & 0 deletions tests/ui/empty_loop_no_std.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
error: empty `loop {}` is currently miscompiled and wastes CPU cycles
--> $DIR/empty_loop_no_std.rs:23:5
|
LL | loop {}
| ^^^^^^^
|
= note: `-D clippy::empty-loop` implied by `-D warnings`
= help: You should either use `panic!()` or add some call to sleep or pause the thread to the loop body.

error: aborting due to previous error

0 comments on commit 53a59e5

Please sign in to comment.