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 | |