@@ -293,18 +293,55 @@ declare_clippy_lint! {
293293declare_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
0 commit comments