@@ -293,18 +293,55 @@ declare_clippy_lint! {
293
293
declare_clippy_lint ! {
294
294
/// **What it does:** Checks for empty `loop` expressions.
295
295
///
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 .
299
299
///
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.
301
338
///
302
339
/// **Example:**
303
340
/// ```no_run
304
341
/// loop {}
305
342
/// ```
306
343
pub EMPTY_LOOP ,
307
- style ,
344
+ correctness ,
308
345
"empty `loop {}`, which should block or sleep"
309
346
}
310
347
@@ -502,14 +539,16 @@ impl<'tcx> LateLintPass<'tcx> for Loops {
502
539
// (even if the "match" or "if let" is used for declaration)
503
540
if let ExprKind :: Loop ( ref block, _, LoopSource :: Loop ) = expr. kind {
504
541
// 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) ;
513
552
}
514
553
515
554
// extract the expression from the first statement (if any) in a block
0 commit comments