26

I get a whole lot of warnings about switches that only partially covers the range of an enumeration switched over. Therefor, I would like to have a "default" for all those switches and put __builtin_unreachable (GCC builtin) in that case, so that the compiler know that case is not reachable.

However, I came to know that GCC4.3 does not support that builtin yet. Is there any good way to emulate that functionality? I thought about dereferencing a null pointer instead, but that may have other undesirable effects/warnings and such. Do you have any better idea?

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212

5 Answers5

12

The upcoming 2023 revision of the C standard (C23, ISO/IEC 9899:2023) is going to have a new macro unreachable

#include <stddef.h>
void unreachable(void);

with the effect of gcc's __builtin_unreachable.

On older C standards, you may be able to call an inline function declared _Noreturn to mark anything after that call as unreachable. The compiler is allowed to throw out any code after such a function. If the function itself is static (and does return), the compiler will usually also inline the function. Here is an example:

static _Noreturn void unreachable() {
    return; /* intentional */
}

/* ... */

foo();
bar(); /* should better not return */
unreachable();
baz(); /* compiler will know this is not reachable */

Notice that you invoke undefined behavior if a function marked _Noreturn indeed returns. Be sure that said function will never be called.

fuz
  • 88,405
  • 25
  • 200
  • 352
  • It warns by saying "Function marked noreturn returns". – Johannes Schaub - litb Mar 15 '14 at 17:05
  • @Johannes Schaub That's right. gcc warns you but accepts the code. As long as your code never actually reached unreachable(), your code should not exhibit undefined behavior. – fuz Mar 15 '14 at 18:18
  • 2
    This is the proper basis of __builtin_unreachable that evolved into the builtin as-is. Disable the specific warning as it is intentionally returning. There may be some further trick other than returning, but this as-is is the equivalent to __builtin_unreachable. Compare https://ideone.com/f8yrLX with https://ideone.com/VP6NOB – TylerY86 May 19 '15 at 17:35
  • 1
    @FUZxxl Shouldn't you call abort() instead of return. Not only it doesn't cause undefined behavior, it is also correct, as the abort function is defined with the _Noreturn function specifier. – 2501 Nov 07 '16 at 20:12
  • @2501 The idea of `__builtin_unreachable()` is to give the compiler a hint it cannot interfere on its own. This objective is not reached by inserting a call to `abort()`. `__builtin_unreachable()` is about optimization, not about safety. – fuz Nov 07 '16 at 20:41
  • @FUZxxl Yeah I just tried abort and it didn't optimize it. – 2501 Nov 07 '16 at 20:49
  • @fuz what is actually the point of the return statement? Is it there just to avoid the compiler optimizing the call to `unreachable()` away completely? – Jesper Matthiesen Dec 12 '17 at 11:55
  • @JesperMatthiesen It's for documentation, it can be removed. – fuz Dec 12 '17 at 12:51
  • My gcc 4.3.3 does not support `_Noreturn`, in fact all I can find says it is supported (in C11) from gcc 4.7. If that is really the case, there's no point since `__builtin_unreachable()` is supported since 4.5 according to the answer below. I suppose `__attribute__ ((noreturn))` could be an alternative!? – Jesper Matthiesen Dec 13 '17 at 15:30
  • @JesperMatthiesen If you are programming for such an old C compiler, you may need to use a cascade of `#if` directives to find the right implementation for your use case. – fuz Nov 17 '22 at 15:26
8

Hmm, something like (since __builtin_unreachable() appeared in 4.5):


#define GCC_VERSION (__GNUC__ * 10000 \
                               + __GNUC_MINOR__ * 100 \
                               + __GNUC_PATCHLEVEL__)
#if GCC_VERSION >= 40500
#define my_unreachable()  __builtin_unreachable()
#else
#define my_unreachable() do { printf("Oh noes!!!111\n"); abort(); } while(0)
#endif

Dietrich Epp
  • 205,541
  • 37
  • 345
  • 415
janneb
  • 36,249
  • 2
  • 81
  • 97
  • 1
    `if (x) y(); else my_unreachable();` does not work with your definition – Nordic Mainframe May 17 '11 at 14:46
  • Can be easily fixed by putting the prinf..abort block in a do/while such as: do { }while(0) – Himadri Choudhury May 17 '11 at 14:56
  • @Luther: Sure, it was just meant as a simple example of how to use the __GNUC_* macros. For a real implementation one would use e.g. the usual do-while(0) trick and maybe do something else in the actual implementation than print "oh no!". – janneb May 17 '11 at 14:59
  • 3
    But this still assumes that the code is reachable. The compiler is not allowed to optimize that printf and that abort away, so it has to emit that code. I want to specifically tell the compiler that the place the macro is put into is unreachable, so that it omits codegen or warnings for all control flow points after it. – Johannes Schaub - litb May 17 '11 at 15:17
  • 5
    @litb: So in effect, you're asking "my compiler doesn't allow me to specify code as unreachable, how do I tell my compiler that code is unreachable". Doesn't really compute. Anyways, at least on my system abort() is marked with __attribute__ ((__noreturn__)) so the compiler should be able to get rid of any code below that call. I guess you still get the warnings though. – janneb May 17 '11 at 16:28
  • 3
    @janneb no, i'm looking for a work-around to achieve similar things to `__builtin_unreachable`. A call to "abort" will not do it, because it has defined behavior, so the compiler still has to put the call to "abort" at that place :( – Johannes Schaub - litb May 18 '11 at 06:37
  • 1
    How sure are you that your enums will never hit that default: case ? It sounds more like it'd be an assert (or an abort()) there in case your assumptions were wrong. \_\_builtin_unreachable() are only for cases where you absolutly, 100% positivly know the code can not ever be reached - and if that builtin is not available, I don't think there's a real substitute, it has to be a compiler intristic. – nos May 18 '11 at 19:58
  • 2
    @nos it will hurt performance. this place has shown to be a bottleneck, so i need to avoid any useless branches and boilerplate the compiler emits on grounds that it thinks that certain code is reachable (and if logic proves that it cannot be reached, perhaps based on higher level logic, not visible by purely analyzing the code statically). – Johannes Schaub - litb May 18 '11 at 20:39
4

Would abort (leaving a core dump) or throw (allowing for alternate data capture) accommodate your needs?

Do you really want to have switch statements that don't cover the full enumeration? I nearly always try to list all the possible cases (to no-op) with no default case so that gcc will warn me if new enumerations are added, as it may be required to handle them rather than letting it silently (during compile) fall into the default.

Mark B
  • 95,107
  • 10
  • 109
  • 188
3

keep it simple:

assert(false);

or, better yet:

#define UNREACHABLE (!"Unreachable code executed!")

assert(UNREACHABLE);
CAFxX
  • 28,060
  • 6
  • 41
  • 66
  • 5
    Too simple. The compiler will still treat anything following the assert as reachable, which can lead to unwanted warnings. – Nordic Mainframe May 17 '11 at 14:54
  • 2
    are you sure? POSIX says that assert() is a macro, and the compiler should be able to statically see that false can never evaluate to true, and therefore turn assert(false) ultimately into a call to abort()... – CAFxX May 18 '11 at 07:43
  • 4
    @CAFxX this is only true if `NDEBUG` is not defined. Assertions are meant to be no-ops in non-debug builds. – Ruslan Feb 21 '16 at 13:56
2
template<unsigned int LINE> class Unreachable_At_Line {}; 
#define __builtin_unreachable() throw Unreachable_At_Line<__LINE__>()

Edit:

Since you want to have unreachable code to be omitted by compiler, below is the simplest way.

#define __builtin_unreachable() { struct X {X& operator=(const X&); } x; x=x; }

Compiler optimizes away x = x; instruction especially when it's unreachable. Here is the usage:

int foo (int i)
{
  switch(i)
  {
  case 0:  return 0;
  case 1:  return 1;
  default: return -1;
  }
  __builtin_unreachable();  // never executed; so compiler optimizes away
}

If you put __builtin_unreachable() in the beginning of foo() then compiler generates a linker error for unimplemented operator =. I ran these tests in gcc 3.4.6 (64-bit).

iammilind
  • 68,093
  • 33
  • 169
  • 336