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