neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

assert_defs.h (5836B)


      1 #pragma once
      2 
      3 #include "auto/config.h"
      4 #include "nvim/log.h"
      5 
      6 // support static asserts (aka compile-time asserts)
      7 
      8 // some compilers don't properly support short-circuiting apparently, giving
      9 // ugly syntax errors when using things like defined(__clang__) &&
     10 // defined(__has_feature) && __has_feature(...). Therefore we define Clang's
     11 // __has_feature and __has_extension macro's before referring to them.
     12 #ifndef __has_feature
     13 # define __has_feature(x) 0
     14 #endif
     15 
     16 #ifndef __has_extension
     17 # define __has_extension __has_feature
     18 #endif
     19 
     20 /// @def STATIC_ASSERT
     21 /// @brief Assert at compile time if condition is not satisfied.
     22 ///
     23 /// Should be put on its own line, followed by a semicolon.
     24 ///
     25 /// Example:
     26 ///
     27 ///     STATIC_ASSERT(sizeof(void *) == 8, "Expected 64-bit mode");
     28 ///
     29 /// @param[in]  condition  Condition to check, should be an integer constant
     30 ///                        expression.
     31 /// @param[in]  message  Message which will be given if check fails.
     32 
     33 /// @def STATIC_ASSERT_EXPR
     34 /// @brief Like #STATIC_ASSERT, but can be used where expressions are used.
     35 ///
     36 /// STATIC_ASSERT_EXPR may be put in brace initializer lists. Error message
     37 /// given in this case is not very nice with the current implementation though
     38 /// and `message` argument is ignored.
     39 
     40 // define STATIC_ASSERT as C11's _Static_assert whenever either C11 mode is
     41 // detected or the compiler is known to support it. Note that Clang in C99
     42 // mode defines __has_feature(c_static_assert) as false and
     43 // __has_extension(c_static_assert) as true. Meaning it does support it, but
     44 // warns. A similar thing goes for gcc, which warns when it's not compiling
     45 // as C11 but does support _Static_assert since 4.6. Since we prefer the
     46 // clearer messages we get from _Static_assert, we suppress the warnings
     47 // temporarily.
     48 
     49 #define STATIC_ASSERT_PRAGMA_START
     50 #define STATIC_ASSERT_PRAGMA_END
     51 #define STATIC_ASSERT(cond, msg) \
     52  do { \
     53    STATIC_ASSERT_PRAGMA_START \
     54    STATIC_ASSERT_STATEMENT(cond, msg); \
     55    STATIC_ASSERT_PRAGMA_END \
     56  } while (0)
     57 
     58 // the easiest case, when the mode is C11 (generic compiler) or Clang
     59 // advertises explicit support for c_static_assert, meaning it won't warn.
     60 #if __STDC_VERSION__ >= 201112 || __has_feature(c_static_assert)
     61 # define STATIC_ASSERT_STATEMENT(cond, msg) _Static_assert(cond, msg)
     62 // if we're dealing with gcc >= 4.6 in C99 mode, we can still use
     63 // _Static_assert but we need to suppress warnings, this is pretty ugly.
     64 #elif (!defined(__clang__) && !defined(__INTEL_COMPILER)) && \
     65  (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
     66 
     67 # define STATIC_ASSERT_STATEMENT(cond, msg) _Static_assert(cond, msg)
     68 
     69 # undef STATIC_ASSERT_PRAGMA_START
     70 
     71 # if __GNUC__ >= 6
     72 #  define STATIC_ASSERT_PRAGMA_START \
     73  _Pragma("GCC diagnostic push") \
     74  _Pragma("GCC diagnostic ignored \"-Wpedantic\"")
     75 # else
     76 #  define STATIC_ASSERT_PRAGMA_START \
     77  _Pragma("GCC diagnostic push") \
     78  _Pragma("GCC diagnostic ignored \"-pedantic\"")
     79 # endif
     80 
     81 # undef STATIC_ASSERT_PRAGMA_END
     82 # define STATIC_ASSERT_PRAGMA_END \
     83  _Pragma("GCC diagnostic pop")
     84 
     85 // the same goes for clang in C99 mode, but we suppress a different warning
     86 #elif defined(__clang__) && __has_extension(c_static_assert)
     87 
     88 # define STATIC_ASSERT_STATEMENT(cond, msg) _Static_assert(cond, msg)
     89 
     90 # undef STATIC_ASSERT_PRAGMA_START
     91 # define STATIC_ASSERT_PRAGMA_START \
     92  _Pragma("clang diagnostic push") \
     93  _Pragma("clang diagnostic ignored \"-Wc11-extensions\"")
     94 
     95 # undef STATIC_ASSERT_PRAGMA_END
     96 # define STATIC_ASSERT_PRAGMA_END \
     97  _Pragma("clang diagnostic pop")
     98 
     99 // TODO(aktau): verify that this works, don't have MSVC on hand.
    100 #elif _MSC_VER >= 1600
    101 
    102 # define STATIC_ASSERT_STATEMENT(cond, msg) static_assert(cond, msg)
    103 
    104 // fallback for compilers that don't support _Static_assert or static_assert
    105 // not as pretty but gets the job done. Credit goes to Pádraig Brady and
    106 // contributors.
    107 #else
    108 # define STATIC_ASSERT_STATEMENT STATIC_ASSERT_EXPR
    109 #endif
    110 
    111 #define ASSERT_CONCAT_(a, b) a##b
    112 #define ASSERT_CONCAT(a, b) ASSERT_CONCAT_(a, b)
    113 // These can't be used after statements in c89.
    114 #ifdef __COUNTER__
    115 # define STATIC_ASSERT_EXPR(e, m) \
    116  ((enum { ASSERT_CONCAT(static_assert_, __COUNTER__) = 1/(!!(e)), }) 0)
    117 #else
    118 // This can't be used twice on the same line so ensure if using in headers
    119 // that the headers are not included twice (by wrapping in #ifndef...#endif)
    120 // Note it doesn't cause an issue when used on same line of separate modules
    121 // compiled with gcc -combine -fwhole-program.
    122 # define STATIC_ASSERT_EXPR(e, m) \
    123  ((enum { ASSERT_CONCAT(assert_line_, __LINE__) = 1/(!!(e)), }) 0)
    124 #endif
    125 
    126 /// @def STRICT_ADD
    127 /// @brief Adds (a + b) and stores result in `c`.  Aborts on overflow.
    128 ///
    129 /// Requires GCC 5+ and Clang 3.8+
    130 ///   https://clang.llvm.org/docs/LanguageExtensions.html
    131 ///   https://gcc.gnu.org/onlinedocs/gcc/Integer-Overflow-Builtins.html
    132 ///
    133 /// Alternative for compilers without __builtin_xx_overflow ?
    134 ///   https://stackoverflow.com/a/44830670/152142
    135 ///
    136 /// @param a  Operand 1.
    137 /// @param b  Operand 2.
    138 /// @param c  Where to store the result.
    139 /// @param t  Result type. Not used if compiler supports __builtin_add_overflow.
    140 #ifdef HAVE_BUILTIN_ADD_OVERFLOW
    141 # define STRICT_ADD(a, b, c, t) \
    142  do { \
    143    if (__builtin_add_overflow(a, b, c)) { \
    144      ELOG("STRICT_ADD overflow"); \
    145      abort(); \
    146    } \
    147  } while (0)
    148 #else
    149 # define STRICT_ADD(a, b, c, t) \
    150  do { *(c) = (t)((a) + (b)); } while (0)
    151 #endif
    152 
    153 /// @def STRICT_SUB
    154 /// @brief Subtracts (a - b) and stores result in `c`.  Aborts on overflow.
    155 /// @see STRICT_ADD
    156 #ifdef HAVE_BUILTIN_ADD_OVERFLOW
    157 # define STRICT_SUB(a, b, c, t) \
    158  do { \
    159    if (__builtin_sub_overflow(a, b, c)) { \
    160      ELOG("STRICT_SUB overflow"); \
    161      abort(); \
    162    } \
    163  } while (0)
    164 #else
    165 # define STRICT_SUB(a, b, c, t) \
    166  do { *(c) = (t)((a) - (b)); } while (0)
    167 #endif