1 #ifndef NVIM_ASSERT_H 2 #define NVIM_ASSERT_H 3 4 #include "auto/config.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__ >= 201112L || __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 168 169 #endif // NVIM_ASSERT_H 170