Skip to content

Commit 6c4257a

Browse files
committed
Auto merge of #6162 - josephlr:empty-loop-no-std, r=flip1995
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
2 parents 3d35072 + 012413d commit 6c4257a

File tree

7 files changed

+82
-21
lines changed

7 files changed

+82
-21
lines changed

clippy_lints/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1570,7 +1570,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
15701570
LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY),
15711571
LintId::of(&len_zero::LEN_ZERO),
15721572
LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING),
1573-
LintId::of(&loops::EMPTY_LOOP),
15741573
LintId::of(&loops::FOR_KV_MAP),
15751574
LintId::of(&loops::NEEDLESS_RANGE_LOOP),
15761575
LintId::of(&loops::SAME_ITEM_PUSH),
@@ -1757,6 +1756,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
17571756
LintId::of(&inline_fn_without_body::INLINE_FN_WITHOUT_BODY),
17581757
LintId::of(&let_underscore::LET_UNDERSCORE_LOCK),
17591758
LintId::of(&literal_representation::MISTYPED_LITERAL_SUFFIXES),
1759+
LintId::of(&loops::EMPTY_LOOP),
17601760
LintId::of(&loops::FOR_LOOPS_OVER_FALLIBLES),
17611761
LintId::of(&loops::ITER_NEXT_LOOP),
17621762
LintId::of(&loops::NEVER_LOOP),

clippy_lints/src/loops.rs

+52-13
Original file line numberDiff line numberDiff line change
@@ -293,18 +293,55 @@ declare_clippy_lint! {
293293
declare_clippy_lint! {
294294
/// **What it does:** Checks for empty `loop` expressions.
295295
///
296-
/// **Why is this bad?** Those busy loops burn CPU cycles without doing
297-
/// anything. Think of the environment and either block on something or at least
298-
/// make the thread sleep for some microseconds.
296+
/// **Why is this bad?** Due to [an LLVM codegen bug](https://github.com/rust-lang/rust/issues/28728)
297+
/// these expressions can be miscompiled or 'optimized' out. Also, these busy loops
298+
/// burn CPU cycles without doing anything.
299299
///
300-
/// **Known problems:** None.
300+
/// It is _almost always_ a better idea to `panic!` than to have a busy loop.
301+
///
302+
/// If panicking isn't possible, think of the environment and either:
303+
/// - block on something
304+
/// - sleep the thread for some microseconds
305+
/// - yield or pause the thread
306+
///
307+
/// For `std` targets, this can be done with
308+
/// [`std::thread::sleep`](https://doc.rust-lang.org/std/thread/fn.sleep.html)
309+
/// or [`std::thread::yield_now`](https://doc.rust-lang.org/std/thread/fn.yield_now.html).
310+
///
311+
/// For `no_std` targets, doing this is more complicated, especially because
312+
/// `#[panic_handler]`s can't panic. To stop/pause the thread, you will
313+
/// probably need to invoke some target-specific intrinsic. Examples include:
314+
/// - [`x86_64::instructions::hlt`](https://docs.rs/x86_64/0.12.2/x86_64/instructions/fn.hlt.html)
315+
/// - [`cortex_m::asm::wfi`](https://docs.rs/cortex-m/0.6.3/cortex_m/asm/fn.wfi.html)
316+
///
317+
/// If you just care about fixing the LLVM bug (and don't care about burning
318+
/// CPU), you can:
319+
/// - On nightly, insert a non-`pure` `asm!` statement. For example:
320+
/// ```rust
321+
/// loop {
322+
/// unsafe { asm!("") };
323+
/// }
324+
/// ```
325+
/// Alternatively, you can compile your code with `-Zinsert-sideeffect`,
326+
/// which will prevent the LLVM from miscompiling your code.
327+
/// - On stable, insert a [`read_volatile`](https://doc.rust-lang.org/core/ptr/fn.read_volatile.html)
328+
/// operation in the loop body. For example:
329+
/// ```rust
330+
/// let dummy = 0u8;
331+
/// loop {
332+
/// unsafe { core::ptr::read_volatile(&dummy) };
333+
/// }
334+
/// ```
335+
///
336+
/// **Known problems:** This is a correctness lint due to the LLVM codegen
337+
/// bug. Once the bug is fixed, this will go back to being a style lint.
301338
///
302339
/// **Example:**
303340
/// ```no_run
304341
/// loop {}
305342
/// ```
306343
pub EMPTY_LOOP,
307-
style,
344+
correctness,
308345
"empty `loop {}`, which should block or sleep"
309346
}
310347

@@ -502,14 +539,16 @@ impl<'tcx> LateLintPass<'tcx> for Loops {
502539
// (even if the "match" or "if let" is used for declaration)
503540
if let ExprKind::Loop(ref block, _, LoopSource::Loop) = expr.kind {
504541
// also check for empty `loop {}` statements
505-
if block.stmts.is_empty() && block.expr.is_none() && !is_no_std_crate(cx.tcx.hir().krate()) {
506-
span_lint(
507-
cx,
508-
EMPTY_LOOP,
509-
expr.span,
510-
"empty `loop {}` detected. You may want to either use `panic!()` or add \
511-
`std::thread::sleep(..);` to the loop body.",
512-
);
542+
if block.stmts.is_empty() && block.expr.is_none() {
543+
let msg = "empty `loop {}` is currently miscompiled and wastes CPU cycles";
544+
let help = if is_no_std_crate(cx.tcx.hir().krate()) {
545+
"You should either use `panic!()` or add some call \
546+
to sleep or pause the thread to the loop body."
547+
} else {
548+
"You should either use `panic!()` or add \
549+
`std::thread::sleep(..);` to the loop body."
550+
};
551+
span_lint_and_help(cx, EMPTY_LOOP, expr.span, msg, None, help);
513552
}
514553

515554
// extract the expression from the first statement (if any) in a block

src/lintlist/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -482,7 +482,7 @@ vec![
482482
},
483483
Lint {
484484
name: "empty_loop",
485-
group: "style",
485+
group: "correctness",
486486
desc: "empty `loop {}`, which should block or sleep",
487487
deprecation: None,
488488
module: "loops",

tests/ui/crashes/ice-360.stderr

+3-2
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,14 @@ LL | | }
1212
|
1313
= note: `-D clippy::while-let-loop` implied by `-D warnings`
1414

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

2324
error: aborting due to 2 previous errors
2425

tests/ui/empty_loop.stderr

+8-3
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,27 @@
1-
error: empty `loop {}` detected. You may want to either use `panic!()` or add `std::thread::sleep(..);` to the loop body.
1+
error: empty `loop {}` is currently miscompiled and wastes CPU cycles
22
--> $DIR/empty_loop.rs:9:5
33
|
44
LL | loop {}
55
| ^^^^^^^
66
|
77
= note: `-D clippy::empty-loop` implied by `-D warnings`
8+
= help: You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body.
89

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

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

2126
error: aborting due to 3 previous errors
2227

tests/ui/issue-3746.rs renamed to tests/ui/empty_loop_no_std.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,16 @@ use core::panic::PanicInfo;
1010

1111
#[start]
1212
fn main(argc: isize, argv: *const *const u8) -> isize {
13-
loop {}
13+
// This loop should not cause a warning.
14+
let dummy = 0u8;
15+
loop {
16+
unsafe { core::ptr::read_volatile(&dummy) };
17+
}
1418
}
1519

1620
#[panic_handler]
1721
fn panic(_info: &PanicInfo) -> ! {
22+
// But this loop will cause a warning.
1823
loop {}
1924
}
2025

tests/ui/empty_loop_no_std.stderr

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error: empty `loop {}` is currently miscompiled and wastes CPU cycles
2+
--> $DIR/empty_loop_no_std.rs:23:5
3+
|
4+
LL | loop {}
5+
| ^^^^^^^
6+
|
7+
= note: `-D clippy::empty-loop` implied by `-D warnings`
8+
= help: You should either use `panic!()` or add some call to sleep or pause the thread to the loop body.
9+
10+
error: aborting due to previous error
11+

0 commit comments

Comments
 (0)