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