Unreachable warnings for debug-only panic

I've got a macro that looks roughly like this:

#[cfg(debug_assertions)]
macro_rules! fatal {
  ($($tt:tt)*) => {
    if true { panic!($($tt)*) }
  };
}

#[cfg(not(debug_assertions))]
macro_rules! fatal {
  ($($tt:tt)*) => {
    ::log::error!($($tt)*)
  };
}

The idea is that for situations where I'd like to panic!(), I can instead use this macro, and I'll avoid taking down my entire app and log instead. But, in debug mode, it'll still force a crash and force me to go debug something. I realize it's not the most common use case, but it works nicely for me.

My problem is that in debug mode, any code after this gets warned as unreachable. This is why I added the if true {} block around the panic. I was wondering how stable that hack is? Is there a better way to silence the unreachable code lint in debug mode?

1 Like

I don’t think there is a better way. if true {} isn't specifically guaranteed to do this, but it would be quite a significant change to the compiler for it to start examining boolean expressions for control flow analysis (which also affects type and borrow checking, so now values would be influencing types).

if true {} is probably the tidiest solution, but you could also hide the panic within a function or closure:

macro_rules! fatal {
  ($($tt:tt)*) => {
    fn panic() { panic!($($tt)*); }
    panic();
  };
}

or

macro_rules! fatal {
  ($($tt:tt)*) => {
    (|| panic!($($tt)*))()
  };
}

It's probably quite stable, because it's also what debug_assert! uses to avoid unused variable warnings in non-debug.

That said, consider doing this instead:

macro_rules! fatal {
  ($($tt:tt)*) => {
    if cfg!(debug_assertions) { panic!($($tt)*) } else { ::log::error!($($tt)*) }
  };
}

so it ensures that both compile regardless of which configuration you're actually running, and even in opt-level=0 codegen the compiler recognizes if const { ... } to avoid emitting the code for the impossible branch.

5 Likes