aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/assert_defs.h
blob: cfc27ee9945f2a2afd08f9d0f841f905048dfc6c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
#pragma once

#include "auto/config.h"
#include "nvim/log.h"

// support static asserts (aka compile-time asserts)

// some compilers don't properly support short-circuiting apparently, giving
// ugly syntax errors when using things like defined(__clang__) &&
// defined(__has_feature) && __has_feature(...). Therefore we define Clang's
// __has_feature and __has_extension macro's before referring to them.
#ifndef __has_feature
# define __has_feature(x) 0
#endif

#ifndef __has_extension
# define __has_extension __has_feature
#endif

/// @def STATIC_ASSERT
/// @brief Assert at compile time if condition is not satisfied.
///
/// Should be put on its own line, followed by a semicolon.
///
/// Example:
///
///     STATIC_ASSERT(sizeof(void *) == 8, "Expected 64-bit mode");
///
/// @param[in]  condition  Condition to check, should be an integer constant
///                        expression.
/// @param[in]  message  Message which will be given if check fails.

/// @def STATIC_ASSERT_EXPR
/// @brief Like #STATIC_ASSERT, but can be used where expressions are used.
///
/// STATIC_ASSERT_EXPR may be put in brace initializer lists. Error message
/// given in this case is not very nice with the current implementation though
/// and `message` argument is ignored.

// define STATIC_ASSERT as C11's _Static_assert whenever either C11 mode is
// detected or the compiler is known to support it. Note that Clang in C99
// mode defines __has_feature(c_static_assert) as false and
// __has_extension(c_static_assert) as true. Meaning it does support it, but
// warns. A similar thing goes for gcc, which warns when it's not compiling
// as C11 but does support _Static_assert since 4.6. Since we prefer the
// clearer messages we get from _Static_assert, we suppress the warnings
// temporarily.

#define STATIC_ASSERT_PRAGMA_START
#define STATIC_ASSERT_PRAGMA_END
#define STATIC_ASSERT(cond, msg) \
  do { \
    STATIC_ASSERT_PRAGMA_START \
    STATIC_ASSERT_STATEMENT(cond, msg); \
    STATIC_ASSERT_PRAGMA_END \
  } while (0)

// the easiest case, when the mode is C11 (generic compiler) or Clang
// advertises explicit support for c_static_assert, meaning it won't warn.
#if __STDC_VERSION__ >= 201112 || __has_feature(c_static_assert)
# define STATIC_ASSERT_STATEMENT(cond, msg) _Static_assert(cond, msg)
// if we're dealing with gcc >= 4.6 in C99 mode, we can still use
// _Static_assert but we need to suppress warnings, this is pretty ugly.
#elif (!defined(__clang__) && !defined(__INTEL_COMPILER)) &&  /* NOLINT(whitespace/parens)*/ \
  (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))

# define STATIC_ASSERT_STATEMENT(cond, msg) _Static_assert(cond, msg)

# undef STATIC_ASSERT_PRAGMA_START

# if __GNUC__ >= 6
#  define STATIC_ASSERT_PRAGMA_START \
  _Pragma("GCC diagnostic push") \
  _Pragma("GCC diagnostic ignored \"-Wpedantic\"")
# else
#  define STATIC_ASSERT_PRAGMA_START \
  _Pragma("GCC diagnostic push") \
  _Pragma("GCC diagnostic ignored \"-pedantic\"")
# endif

# undef STATIC_ASSERT_PRAGMA_END
# define STATIC_ASSERT_PRAGMA_END \
  _Pragma("GCC diagnostic pop")

// the same goes for clang in C99 mode, but we suppress a different warning
#elif defined(__clang__) && __has_extension(c_static_assert)

# define STATIC_ASSERT_STATEMENT(cond, msg) _Static_assert(cond, msg)

# undef STATIC_ASSERT_PRAGMA_START
# define STATIC_ASSERT_PRAGMA_START \
  _Pragma("clang diagnostic push") \
  _Pragma("clang diagnostic ignored \"-Wc11-extensions\"")

# undef STATIC_ASSERT_PRAGMA_END
# define STATIC_ASSERT_PRAGMA_END \
  _Pragma("clang diagnostic pop")

// TODO(aktau): verify that this works, don't have MSVC on hand.
#elif _MSC_VER >= 1600

# define STATIC_ASSERT_STATEMENT(cond, msg) static_assert(cond, msg)

// fallback for compilers that don't support _Static_assert or static_assert
// not as pretty but gets the job done. Credit goes to Pádraig Brady and
// contributors.
#else
# define STATIC_ASSERT_STATEMENT STATIC_ASSERT_EXPR
#endif

#define ASSERT_CONCAT_(a, b) a##b
#define ASSERT_CONCAT(a, b) ASSERT_CONCAT_(a, b)
// These can't be used after statements in c89.
#ifdef __COUNTER__
# define STATIC_ASSERT_EXPR(e, m) \
  ((enum { ASSERT_CONCAT(static_assert_, __COUNTER__) = 1/(!!(e)), }) 0)
#else
// This can't be used twice on the same line so ensure if using in headers
// that the headers are not included twice (by wrapping in #ifndef...#endif)
// Note it doesn't cause an issue when used on same line of separate modules
// compiled with gcc -combine -fwhole-program.
# define STATIC_ASSERT_EXPR(e, m) \
  ((enum { ASSERT_CONCAT(assert_line_, __LINE__) = 1/(!!(e)), }) 0)
#endif

/// @def STRICT_ADD
/// @brief Adds (a + b) and stores result in `c`.  Aborts on overflow.
///
/// Requires GCC 5+ and Clang 3.8+
///   https://clang.llvm.org/docs/LanguageExtensions.html
///   https://gcc.gnu.org/onlinedocs/gcc/Integer-Overflow-Builtins.html
///
/// Alternative for compilers without __builtin_xx_overflow ?
///   https://stackoverflow.com/a/44830670/152142
///
/// @param a  Operand 1.
/// @param b  Operand 2.
/// @param c  Where to store the result.
/// @param t  Result type. Not used if compiler supports __builtin_add_overflow.
#ifdef HAVE_BUILTIN_ADD_OVERFLOW
# define STRICT_ADD(a, b, c, t) \
  do { \
    if (__builtin_add_overflow(a, b, c)) { \
      ELOG("STRICT_ADD overflow"); \
      abort(); \
    } \
  } while (0)
#else
# define STRICT_ADD(a, b, c, t) \
  do { *(c) = (t)((a) + (b)); } while (0)
#endif

/// @def STRICT_SUB
/// @brief Subtracts (a - b) and stores result in `c`.  Aborts on overflow.
/// @see STRICT_ADD
#ifdef HAVE_BUILTIN_ADD_OVERFLOW
# define STRICT_SUB(a, b, c, t) \
  do { \
    if (__builtin_sub_overflow(a, b, c)) { \
      ELOG("STRICT_SUB overflow"); \
      abort(); \
    } \
  } while (0)
#else
# define STRICT_SUB(a, b, c, t) \
  do { *(c) = (t)((a) - (b)); } while (0)
#endif