Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

std::unreachable is actually a quite useful optimisation tool. For instance if you are sure that a switch-case default branch is actually unreachable you can put a std::unreachable into the default-branch to hint the compiler that it may remove a range check in front of the switch-case jump table access (since you promised to the compiler that the default branch is never reached).

It's a double edged sword of course. If control flow actually hits the default branch, then all bets are off (because that means the code will access an out-of-range jump table slot and jump somewhere into the wilderness).

AFAIK compilers are free to perform something like Rust's panic when std::unreachable is actually hit, but that makes only sense in debug mode. Because in the above example, the compiler would need to add a range check to figure out if panic needs to be called and that would completely defeat the idea of removing that range check in the first place.



I recommend omitting the default case and putting std::unreachable() outside the switch and omitting the default label for the sole reason that compilers are more likely to warn for a missed case label this way (-Wswitch vs -Wswitch-enum in gcc/clang, the former is included in -Wall, the latter isn't included even in -Wextra).

This also allows expressing intent: no default label means that I meant to handle all cases, and having a default means that I opted into a fallback, please don't warn. That's probably why -Wswitch-enum isn't enabled by default, too many false positives without a convenient way to suppress the warning.


Hmm, how would that look like? Like this?

    if ((val < min) || (val >= max)) {
        __builtin_unreachable();
    }
    switch (val) {
        ...
    }
I haven't actually tried that, but if it works as intended it would actually be better yeah (not really in the case where I'm using it, because I'm switching on an integer, not an enum).


More like:

  switch (e) {
    case A:
      foo(); break;
    case B:
      bar(); break;
  }
  std::unreachable();
instead of:

  switch (e) {
    case A:
      foo(); break;
    case B:
      bar(); break;
    default:
      std::unreachable();
  }
The former is more likely to produce a warning if there is an enumeration C that you forgot to handle, or you added an enumeration C and missed a switch-case to update.

edit:

duh, it's supposed to be returns here instead of breaks.


But then all case branches hit the std::unreachable. How can that work in practice?


I think it's supposed to be a return not a break.


yeah, thanks! I added an edit.


This is so funny that you have just demonstrated how stupid this construction is in practice by making a mistake.


Having it available means we can use it explicitly. For example, I could see a compiler flag making `std::vector<T>::operator[]` be checked and then if profiling warrants, remove the check by explicitly checking if my index is out of bounds and invoking UB. Not saying that’s the pattern people will use, but having an escape hatch makes safer-by-default behavior more approachable.


>For instance if you are sure that a switch-case default branch is actually unreachable you can put a std::unreachable into the default-branch to hint the compiler that it may remove a range check in front of the switch-case jump table access

I guess they stole this idea from Terry Davis who implemented this in holyC. When you use square brackets it just doesn't do any range check. Terry, as always, been ahead of the times. Like so:

switch[abc] { [...] }


Tbf it's a fairly obvious idea when looking at the compiler output for a large switch-case statement.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: