xref: /aosp_15_r20/external/pigweed/pw_assert/public/pw_assert/internal/check_impl.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2020 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 #pragma once
15 
16 #ifdef __cplusplus
17 #include <type_traits>
18 #else
19 #include <stddef.h>
20 #endif  // __cplusplus
21 
22 // Note: This file depends on the backend header already being included.
23 
24 #include "pw_assert/config.h"
25 #include "pw_preprocessor/compiler.h"
26 
27 // PW_CRASH - Crash the system, with a message.
28 #define PW_CRASH(...)                                                        \
29   do {                                                                       \
30     if (0) { /* Check args but don't execute to avoid multiple evaluation */ \
31       _pw_assert_CheckMessageArguments(" " __VA_ARGS__);                     \
32     }                                                                        \
33     PW_HANDLE_CRASH(__VA_ARGS__);                                            \
34   } while (0)
35 
36 // PW_CHECK - If condition evaluates to false, crash. Message optional.
37 #define PW_CHECK(condition, ...)                                               \
38   do {                                                                         \
39     if (!(condition)) {                                                        \
40       _pw_assert_ConditionCannotContainThePercentCharacter(                    \
41           #condition); /* cannot use '%' in PW_CHECK conditions */             \
42       if (0) { /* Check args but don't execute to avoid multiple evaluation */ \
43         _pw_assert_CheckMessageArguments(" " __VA_ARGS__);                     \
44       }                                                                        \
45       PW_HANDLE_ASSERT_FAILURE(#condition, "" __VA_ARGS__);                    \
46     }                                                                          \
47   } while (0)
48 
49 #define PW_DCHECK(...)            \
50   do {                            \
51     if (PW_ASSERT_ENABLE_DEBUG) { \
52       PW_CHECK(__VA_ARGS__);      \
53     }                             \
54   } while (0)
55 
56 // PW_D?CHECK_<type>_<comparison> macros - Binary comparison asserts.
57 //
58 // The below blocks are structured in table form, violating the 80-column
59 // Pigweed style, in order to make it clearer what is common and what isn't
60 // between the multitude of assert macro instantiations. To best view this
61 // section, turn off editor wrapping or make your editor wide.
62 //
63 // clang-format off
64 
65 // Checks for int: LE, LT, GE, GT, EQ.
66 #define PW_CHECK_INT_LE(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, <=, argb, int, "%d", __VA_ARGS__)
67 #define PW_CHECK_INT_LT(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, < , argb, int, "%d", __VA_ARGS__)
68 #define PW_CHECK_INT_GE(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, >=, argb, int, "%d", __VA_ARGS__)
69 #define PW_CHECK_INT_GT(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, > , argb, int, "%d", __VA_ARGS__)
70 #define PW_CHECK_INT_EQ(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, ==, argb, int, "%d", __VA_ARGS__)
71 #define PW_CHECK_INT_NE(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, !=, argb, int, "%d", __VA_ARGS__)
72 
73 // Debug checks for int: LE, LT, GE, GT, EQ.
74 #define PW_DCHECK_INT_LE(...) if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_INT_LE(__VA_ARGS__)
75 #define PW_DCHECK_INT_LT(...) if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_INT_LT(__VA_ARGS__)
76 #define PW_DCHECK_INT_GE(...) if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_INT_GE(__VA_ARGS__)
77 #define PW_DCHECK_INT_GT(...) if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_INT_GT(__VA_ARGS__)
78 #define PW_DCHECK_INT_EQ(...) if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_INT_EQ(__VA_ARGS__)
79 #define PW_DCHECK_INT_NE(...) if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_INT_NE(__VA_ARGS__)
80 
81 // Checks for unsigned int: LE, LT, GE, GT, EQ.
82 #define PW_CHECK_UINT_LE(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, <=, argb, unsigned int, "%u", __VA_ARGS__)
83 #define PW_CHECK_UINT_LT(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, < , argb, unsigned int, "%u", __VA_ARGS__)
84 #define PW_CHECK_UINT_GE(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, >=, argb, unsigned int, "%u", __VA_ARGS__)
85 #define PW_CHECK_UINT_GT(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, > , argb, unsigned int, "%u", __VA_ARGS__)
86 #define PW_CHECK_UINT_EQ(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, ==, argb, unsigned int, "%u", __VA_ARGS__)
87 #define PW_CHECK_UINT_NE(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, !=, argb, unsigned int, "%u", __VA_ARGS__)
88 
89 // Debug checks for unsigned int: LE, LT, GE, GT, EQ.
90 #define PW_DCHECK_UINT_LE(...) if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_UINT_LE(__VA_ARGS__)
91 #define PW_DCHECK_UINT_LT(...) if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_UINT_LT(__VA_ARGS__)
92 #define PW_DCHECK_UINT_GE(...) if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_UINT_GE(__VA_ARGS__)
93 #define PW_DCHECK_UINT_GT(...) if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_UINT_GT(__VA_ARGS__)
94 #define PW_DCHECK_UINT_EQ(...) if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_UINT_EQ(__VA_ARGS__)
95 #define PW_DCHECK_UINT_NE(...) if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_UINT_NE(__VA_ARGS__)
96 
97 // Checks for pointer: LE, LT, GE, GT, EQ, NE.
98 #define PW_CHECK_PTR_LE(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, <=, argb, const void*, "%p", __VA_ARGS__)
99 #define PW_CHECK_PTR_LT(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, < , argb, const void*, "%p", __VA_ARGS__)
100 #define PW_CHECK_PTR_GE(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, >=, argb, const void*, "%p", __VA_ARGS__)
101 #define PW_CHECK_PTR_GT(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, > , argb, const void*, "%p", __VA_ARGS__)
102 #define PW_CHECK_PTR_EQ(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, ==, argb, const void*, "%p", __VA_ARGS__)
103 #define PW_CHECK_PTR_NE(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, !=, argb, const void*, "%p", __VA_ARGS__)
104 
105 // Check for pointer: NOTNULL. Use "nullptr" in C++, "NULL" in C.
106 #ifdef __cplusplus
107 #define PW_CHECK_NOTNULL(arga, ...) \
108   _PW_CHECK_BINARY_CMP_IMPL(arga, !=, nullptr, const void*, "%p", __VA_ARGS__)
109 #else  // __cplusplus
110 #define PW_CHECK_NOTNULL(arga, ...) \
111   _PW_CHECK_BINARY_CMP_IMPL(arga, !=, NULL, const void*, "%p", __VA_ARGS__)
112 #endif  // __cplusplus
113 
114 // Debug checks for pointer: LE, LT, GE, GT, EQ, NE, and NOTNULL.
115 #define PW_DCHECK_PTR_LE(...)  if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_PTR_LE(__VA_ARGS__)
116 #define PW_DCHECK_PTR_LT(...)  if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_PTR_LT(__VA_ARGS__)
117 #define PW_DCHECK_PTR_GE(...)  if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_PTR_GE(__VA_ARGS__)
118 #define PW_DCHECK_PTR_GT(...)  if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_PTR_GT(__VA_ARGS__)
119 #define PW_DCHECK_PTR_EQ(...)  if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_PTR_EQ(__VA_ARGS__)
120 #define PW_DCHECK_PTR_NE(...)  if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_PTR_NE(__VA_ARGS__)
121 #define PW_DCHECK_NOTNULL(...) if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_NOTNULL(__VA_ARGS__)
122 
123 // Checks for float: EXACT_LE, EXACT_LT, EXACT_GE, EXACT_GT, EXACT_EQ, EXACT_NE,
124 // NEAR.
125 #define PW_CHECK_FLOAT_NEAR(arga, argb, abs_tolerance, ...) \
126   _PW_CHECK_FLOAT_NEAR(arga, argb, abs_tolerance, __VA_ARGS__)
127 #define PW_CHECK_FLOAT_EXACT_LE(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, <=, argb, float, "%f", __VA_ARGS__)
128 #define PW_CHECK_FLOAT_EXACT_LT(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, < , argb, float, "%f", __VA_ARGS__)
129 #define PW_CHECK_FLOAT_EXACT_GE(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, >=, argb, float, "%f", __VA_ARGS__)
130 #define PW_CHECK_FLOAT_EXACT_GT(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, > , argb, float, "%f", __VA_ARGS__)
131 #define PW_CHECK_FLOAT_EXACT_EQ(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, ==, argb, float, "%f", __VA_ARGS__)
132 #define PW_CHECK_FLOAT_EXACT_NE(arga, argb, ...) _PW_CHECK_BINARY_CMP_IMPL(arga, !=, argb, float, "%f", __VA_ARGS__)
133 
134 // Debug checks for float: NEAR, EXACT_LE, EXACT_LT, EXACT_GE, EXACT_GT,
135 // EXACT_EQ.
136 #define PW_DCHECK_FLOAT_NEAR(...)     if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_FLOAT_NEAR(__VA_ARGS__)
137 #define PW_DCHECK_FLOAT_EXACT_LE(...) if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_FLOAT_EXACT_LE(__VA_ARGS__)
138 #define PW_DCHECK_FLOAT_EXACT_LT(...) if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_FLOAT_EXACT_LT(__VA_ARGS__)
139 #define PW_DCHECK_FLOAT_EXACT_GE(...) if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_FLOAT_EXACT_GE(__VA_ARGS__)
140 #define PW_DCHECK_FLOAT_EXACT_GT(...) if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_FLOAT_EXACT_GT(__VA_ARGS__)
141 #define PW_DCHECK_FLOAT_EXACT_EQ(...) if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_FLOAT_EXACT_EQ(__VA_ARGS__)
142 #define PW_DCHECK_FLOAT_EXACT_NE(...) if (!(PW_ASSERT_ENABLE_DEBUG)) {} else PW_CHECK_FLOAT_EXACT_NE(__VA_ARGS__)
143 
144 // Debug checks for integer overflows: ADD, SUB, MUL.
145 #define PW_CHECK_ADD(a, b, out, ...) PW_CHECK(!PW_ADD_OVERFLOW(a, b, out), __VA_ARGS__)
146 #define PW_CHECK_SUB(a, b, out, ...) PW_CHECK(!PW_SUB_OVERFLOW(a, b, out), __VA_ARGS__)
147 #define PW_CHECK_MUL(a, b, out, ...) PW_CHECK(!PW_MUL_OVERFLOW(a, b, out), __VA_ARGS__)
148 
149 // Debug checks for integer overflows: ADD, SUB, MUL.
150 #define PW_DCHECK_ADD(a, b, out, ...) PW_DCHECK(!PW_ADD_OVERFLOW(a, b, out), __VA_ARGS__)
151 #define PW_DCHECK_SUB(a, b, out, ...) PW_DCHECK(!PW_SUB_OVERFLOW(a, b, out), __VA_ARGS__)
152 #define PW_DCHECK_MUL(a, b, out, ...) PW_DCHECK(!PW_MUL_OVERFLOW(a, b, out), __VA_ARGS__)
153 
154 // clang-format on
155 
156 // PW_CHECK_OK - If expression does not evaluate to PW_STATUS_OK, crash.
157 // Message optional.
158 //
159 // In C++, expression must evaluate to a value convertible to Status via
160 // pw::internal::ConvertToStatus().
161 //
162 // In C, expression must evaluate to a pw_Status value.
163 #define PW_CHECK_OK(expression, ...)                       \
164   do {                                                     \
165     const _PW_CHECK_OK_STATUS _pw_assert_check_ok_status = \
166         _PW_CHECK_OK_TO_STATUS(expression);                \
167     if (_pw_assert_check_ok_status != PW_STATUS_OK) {      \
168       _PW_CHECK_BINARY_ARG_HANDLER(                        \
169           #expression,                                     \
170           pw_StatusString(_pw_assert_check_ok_status),     \
171           "==",                                            \
172           "OkStatus()",                                    \
173           "OK",                                            \
174           "%s",                                            \
175           "" __VA_ARGS__);                                 \
176     }                                                      \
177   } while (0)
178 
179 #ifdef __cplusplus
180 #define _PW_CHECK_OK_STATUS ::pw::Status
181 #define _PW_CHECK_OK_TO_STATUS(expr) ::pw::internal::ConvertToStatus(expr)
182 #else
183 #define _PW_CHECK_OK_STATUS pw_Status
184 #define _PW_CHECK_OK_TO_STATUS(expr) (expr)
185 #endif  // __cplusplus
186 
187 #define PW_DCHECK_OK(...)          \
188   if (!(PW_ASSERT_ENABLE_DEBUG)) { \
189   } else                           \
190     PW_CHECK_OK(__VA_ARGS__)
191 
192 // Use a static_cast in C++ to avoid accidental comparisons between e.g. an
193 // integer and the CHECK message const char*.
194 #if defined(__cplusplus) && __cplusplus >= 201703L
195 
196 namespace pw::assert::internal {
197 
198 template <typename T, typename U>
ConvertToType(U * value)199 constexpr const void* ConvertToType(U* value) {
200   if constexpr (std::is_function<U>()) {
201     return reinterpret_cast<const void*>(value);
202   } else {
203     return static_cast<const void*>(value);
204   }
205 }
206 
207 template <typename T, typename U>
ConvertToType(const U & value)208 constexpr T ConvertToType(const U& value) {
209   return static_cast<T>(value);
210 }
211 
212 }  // namespace pw::assert::internal
213 
214 #define _PW_CHECK_CONVERT(type, name, arg) \
215   type name = ::pw::assert::internal::ConvertToType<type>(arg)
216 #else
217 #define _PW_CHECK_CONVERT(type, name, arg) type name = (type)(arg)
218 #endif  // __cplusplus
219 
220 // For the binary assertions, this private macro is re-used for almost all of
221 // the variants. Due to limitations of C formatting, it is necessary to have
222 // separate macros for the types.
223 //
224 // The macro avoids evaluating the arguments multiple times at the cost of some
225 // macro complexity.
226 #define _PW_CHECK_BINARY_CMP_IMPL(                                    \
227     arg_a, comparison_op, arg_b, type_decl, type_fmt, ...)            \
228   do {                                                                \
229     _PW_CHECK_CONVERT(type_decl, evaluated_argument_a, arg_a);        \
230     _PW_CHECK_CONVERT(type_decl, evaluated_argument_b, arg_b);        \
231     if (!(evaluated_argument_a comparison_op evaluated_argument_b)) { \
232       _PW_CHECK_BINARY_ARG_HANDLER(#arg_a,                            \
233                                    evaluated_argument_a,              \
234                                    #comparison_op,                    \
235                                    #arg_b,                            \
236                                    evaluated_argument_b,              \
237                                    type_fmt,                          \
238                                    "" __VA_ARGS__);                   \
239     }                                                                 \
240   } while (0)
241 
242 // All binary comparison CHECK macros are directed to this handler before
243 // hitting the CHECK backend. This controls whether evaluated values are
244 // captured.
245 #if PW_ASSERT_CAPTURE_VALUES
246 #define _PW_CHECK_BINARY_ARG_HANDLER(arg_a_str,                            \
247                                      arg_a_val,                            \
248                                      comparison_op_str,                    \
249                                      arg_b_str,                            \
250                                      arg_b_val,                            \
251                                      type_fmt,                             \
252                                      ...)                                  \
253                                                                            \
254   _pw_assert_ConditionCannotContainThePercentCharacter(                    \
255       arg_a_str arg_b_str); /* cannot use '%' in PW_CHECK conditions */    \
256   if (0) { /* Check args but don't execute to avoid multiple evaluation */ \
257     _pw_assert_CheckMessageArguments(" " __VA_ARGS__);                     \
258   }                                                                        \
259   PW_HANDLE_ASSERT_BINARY_COMPARE_FAILURE(arg_a_str,                       \
260                                           arg_a_val,                       \
261                                           comparison_op_str,               \
262                                           arg_b_str,                       \
263                                           arg_b_val,                       \
264                                           type_fmt,                        \
265                                           __VA_ARGS__)
266 #else
267 #define _PW_CHECK_BINARY_ARG_HANDLER(arg_a_str,                            \
268                                      arg_a_val,                            \
269                                      comparison_op_str,                    \
270                                      arg_b_str,                            \
271                                      arg_b_val,                            \
272                                      type_fmt,                             \
273                                      ...)                                  \
274   _pw_assert_ConditionCannotContainThePercentCharacter(                    \
275       arg_a_str arg_b_str); /* cannot use '%' in PW_CHECK conditions */    \
276   if (0) { /* Check args but don't execute to avoid multiple evaluation */ \
277     _pw_assert_CheckMessageArguments(" " __VA_ARGS__);                     \
278   }                                                                        \
279   PW_HANDLE_ASSERT_FAILURE(arg_a_str " " comparison_op_str " " arg_b_str,  \
280                            __VA_ARGS__)
281 #endif  // PW_ASSERT_CAPTURE_VALUES
282 
283 // Custom implementation for FLOAT_NEAR which is implemented through two
284 // underlying checks which are not trivially replaced through the use of
285 // FLOAT_EXACT_LE & FLOAT_EXACT_GE.
286 #define _PW_CHECK_FLOAT_NEAR(argument_a, argument_b, abs_tolerance, ...)  \
287   do {                                                                    \
288     PW_CHECK_FLOAT_EXACT_GE(abs_tolerance, 0.0f);                         \
289     float evaluated_argument_a = (float)(argument_a);                     \
290     float evaluated_argument_b_min = (float)(argument_b) - abs_tolerance; \
291     float evaluated_argument_b_max = (float)(argument_b) + abs_tolerance; \
292     if (!(evaluated_argument_a >= evaluated_argument_b_min)) {            \
293       _PW_CHECK_BINARY_ARG_HANDLER(#argument_a,                           \
294                                    evaluated_argument_a,                  \
295                                    ">=",                                  \
296                                    #argument_b " - abs_tolerance",        \
297                                    evaluated_argument_b_min,              \
298                                    "%f",                                  \
299                                    "" __VA_ARGS__);                       \
300     } else if (!(evaluated_argument_a <= evaluated_argument_b_max)) {     \
301       _PW_CHECK_BINARY_ARG_HANDLER(#argument_a,                           \
302                                    evaluated_argument_a,                  \
303                                    "<=",                                  \
304                                    #argument_b " + abs_tolerance",        \
305                                    evaluated_argument_b_max,              \
306                                    "%f",                                  \
307                                    "" __VA_ARGS__);                       \
308     }                                                                     \
309   } while (0)
310 
311 // This empty function allows the compiler to verify that the condition contains
312 // no % characters (modulus operator). Backends (pw_assert-tokenized in
313 // particular) may include the condition in the format string as a size
314 // optimization. Unintentionally introducing an extra argument could lead to
315 // problems. Checking the condition here ensures that the behavior is consistent
316 // for all backends.
317 //
318 // TODO: b/235149326 - Remove this restriction when pw_assert macros no longer
319 // accept arbitrary arguments.
320 static inline void _pw_assert_ConditionCannotContainThePercentCharacter(
321     const char* format, ...) PW_PRINTF_FORMAT(1, 2);
322 
_pw_assert_ConditionCannotContainThePercentCharacter(const char * format,...)323 static inline void _pw_assert_ConditionCannotContainThePercentCharacter(
324     const char* format, ...) {
325   (void)format;
326 }
327 
328 // Empty function for checking that arguments match the format string. This
329 // function also ensures arguments are considered "used" in PW_CHECK, even if
330 // the backend does not use them.
331 static inline void _pw_assert_CheckMessageArguments(const char* format, ...)
332     PW_PRINTF_FORMAT(1, 2);
333 
_pw_assert_CheckMessageArguments(const char * format,...)334 static inline void _pw_assert_CheckMessageArguments(const char* format, ...) {
335   (void)format;
336 }
337