Line data Source code
1 1 : // Distributed under the MIT License.
2 : // See LICENSE.txt for details.
3 :
4 : /// \file
5 : /// Defines macro ASSERT.
6 :
7 : #pragma once
8 :
9 : #include <iomanip>
10 : #include <sstream>
11 : #include <string>
12 :
13 : #include "Utilities/ErrorHandling/AbortWithErrorMessage.hpp"
14 : #include "Utilities/ErrorHandling/FloatingPointExceptions.hpp"
15 : #include "Utilities/Kokkos/KokkosMacros.hpp"
16 :
17 : /*!
18 : * \ingroup ErrorHandlingGroup
19 : * \brief Assert that an expression should be true.
20 : *
21 : * If the preprocessor macro SPECTRE_DEBUG is defined and the expression is
22 : * false, an error message is printed to the standard error stream, and the
23 : * program aborts. ASSERT should be used to catch coding errors as it does
24 : * nothing in production code.
25 : *
26 : * ASSERT can also be used in functions that run on a GPU (`KOKKOS_FUNCTION`s).
27 : * On the GPU the error message can't be printed, so a failed assertion only
28 : * reports the expression, file and line.
29 : *
30 : * \param a the expression that must be true
31 : * \param m the error message as an ostream
32 : */
33 : #ifdef SPECTRE_DEBUG
34 : // isocpp.org recommends using an `if (true)` instead of a `do
35 : // while(false)` for macros because the latter can mess with inlining
36 : // in some (old?) compilers:
37 : // https://isocpp.org/wiki/faq/misc-technical-issues#macros-with-multi-stmts
38 : // https://isocpp.org/wiki/faq/misc-technical-issues#macros-with-if
39 : // However, Intel's reachability analyzer (as of version 16.0.3
40 : // 20160415) can't figure out that the else branch and everything
41 : // after it is unreachable, causing warnings (and possibly suboptimal
42 : // code generation).
43 : #define SPECTRE_ASSERT_ON_HOST(a, m) \
44 : do { \
45 : if (!(a)) { /* NOLINT(readability-simplify-boolean-expr) */ \
46 : const ScopedFpeState disable_fpes_ASSERT(false); \
47 : std::ostringstream avoid_name_collisions_ASSERT; \
48 : /* clang-tidy: macro arg in parentheses */ \
49 : avoid_name_collisions_ASSERT << std::setprecision(18) << std::scientific \
50 : << m; /* NOLINT */ \
51 : abort_with_error_message(#a, __FILE__, __LINE__, \
52 : static_cast<const char*>(__PRETTY_FUNCTION__), \
53 : avoid_name_collisions_ASSERT.str()); \
54 : } \
55 : } while (false)
56 : #else
57 1 : #define SPECTRE_ASSERT_ON_HOST(a, m) \
58 : do { \
59 : if (false) { \
60 : static_cast<void>(a); \
61 : const ScopedFpeState disable_fpes_ASSERT(false); \
62 : std::ostringstream avoid_name_collisions_ASSERT; \
63 : /* clang-tidy: macro arg in parentheses */ \
64 : avoid_name_collisions_ASSERT << std::setprecision(18) << std::scientific \
65 : << m; /* NOLINT */ \
66 : static_cast<void>(avoid_name_collisions_ASSERT); \
67 : } \
68 : } while (false)
69 : #endif
70 :
71 : #ifdef SPECTRE_KOKKOS
72 : // In code that is compiled for a GPU, the message can't be streamed and
73 : // printed, so only the expression, file and line are reported there. Kokkos
74 : // compiles only one of the branches for the host and the GPU, respectively.
75 0 : #define SPECTRE_ASSERT_STRINGIFY_IMPL(x) #x
76 0 : #define SPECTRE_ASSERT_STRINGIFY(x) SPECTRE_ASSERT_STRINGIFY_IMPL(x)
77 0 : #define SPECTRE_ASSERT_DEVICE_MESSAGE(a) \
78 : "ASSERT failed on the device: " a "\nat " __FILE__ \
79 : ":" SPECTRE_ASSERT_STRINGIFY(__LINE__) "\nRun on the host for the " \
80 : "full error message.\n"
81 : #ifdef SPECTRE_DEBUG
82 : #define ASSERT(a, m) \
83 : do { \
84 : KOKKOS_IF_ON_HOST((SPECTRE_ASSERT_ON_HOST(a, m);)) \
85 : KOKKOS_IF_ON_DEVICE( \
86 : (if (not(a)) { ::Kokkos::abort(SPECTRE_ASSERT_DEVICE_MESSAGE(#a)); })) \
87 : } while (false)
88 : #else
89 0 : #define ASSERT(a, m) \
90 : do { \
91 : KOKKOS_IF_ON_HOST((SPECTRE_ASSERT_ON_HOST(a, m);)) \
92 : } while (false)
93 : #endif
94 : #else
95 : #define ASSERT(a, m) SPECTRE_ASSERT_ON_HOST(a, m)
96 : #endif
|