GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/base/check.h Lines: 3 3 100.0 %
Date: 2021-08-16 Branches: 0 0 0.0 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Tim King, Mathias Preiner, Andres Noetzli
4
 *
5
 * This file is part of the cvc5 project.
6
 *
7
 * Copyright (c) 2009-2021 by the authors listed in the file AUTHORS
8
 * in the top-level source directory and their institutional affiliations.
9
 * All rights reserved.  See the file COPYING in the top-level source
10
 * directory for licensing information.
11
 * ****************************************************************************
12
 *
13
 * Assertion utility classes, functions and macros.
14
 *
15
 * Assertion macros assert a condition and aborts() the process if the
16
 * condition is not satisfied. These macro leave a hanging ostream for the user
17
 * to specify additional information about the failure.
18
 *
19
 * Example usage:
20
 *   AlwaysAssert(x >= 0) << "x must be positive.";
21
 *
22
 * Assert is an AlwaysAssert that is only enabled in debug builds.
23
 *   Assert(pointer != nullptr);
24
 *
25
 * CVC5_FATAL() can be used to indicate unreachable code.
26
 *
27
 * Note: The AlwaysAssert and Assert macros are not safe for use in
28
 *       signal-handling code.
29
 */
30
31
#include "cvc5_private_library.h"
32
33
#ifndef CVC5__CHECK_H
34
#define CVC5__CHECK_H
35
36
#include <cstdarg>
37
#include <ostream>
38
39
#include "base/exception.h"
40
#include "cvc5_export.h"
41
42
// Define CVC5_PREDICT_FALSE(x) that helps the compiler predict that x will be
43
// false (if there is compiler support).
44
#ifdef __has_builtin
45
#if __has_builtin(__builtin_expect)
46
#define CVC5_PREDICT_FALSE(x) (__builtin_expect(x, false))
47
#define CVC5_PREDICT_TRUE(x) (__builtin_expect(x, true))
48
#else
49
#define CVC5_PREDICT_FALSE(x) x
50
#define CVC5_PREDICT_TRUE(x) x
51
#endif
52
#else
53
#define CVC5_PREDICT_FALSE(x) x
54
#define CVC5_PREDICT_TRUE(x) x
55
#endif
56
57
#ifdef __has_cpp_attribute
58
#if __has_cpp_attribute(fallthrough)
59
#define CVC5_FALLTHROUGH [[fallthrough]]
60
#endif // __has_cpp_attribute(fallthrough)
61
#endif // __has_cpp_attribute
62
#ifndef CVC5_FALLTHROUGH
63
#define CVC5_FALLTHROUGH
64
#endif
65
66
namespace cvc5 {
67
68
// Implementation notes:
69
// To understand FatalStream and OStreamVoider, it is useful to understand
70
// how a AlwaysAssert is structured. AlwaysAssert(cond) is roughly the following
71
// pattern:
72
//  cond ? (void)0 : OstreamVoider() & FatalStream().stream()
73
// This is a carefully crafted message to achieve a hanging ostream using
74
// operator precedence. The line `AlwaysAssert(cond) << foo << bar;` will bind
75
// as follows:
76
//  `cond ? ((void)0) : (OSV() & ((FS().stream() << foo) << bar));`
77
// Once the expression is evaluated, the destructor ~FatalStream() of the
78
// temporary object is then run, which abort()'s the process. The role of the
79
// OStreamVoider() is to match the void type of the true branch.
80
81
// Class that provides an ostream and whose destructor aborts! Direct usage of
82
// this class is discouraged.
83
class CVC5_EXPORT FatalStream
84
{
85
 public:
86
  FatalStream(const char* function, const char* file, int line);
87
  [[noreturn]] ~FatalStream();
88
89
  std::ostream& stream();
90
91
 private:
92
  void Flush();
93
};
94
95
// Helper class that changes the type of an std::ostream& into a void. See
96
// "Implementation notes" for more information.
97
class OstreamVoider
98
{
99
 public:
100
810
  OstreamVoider() {}
101
  // The operator precedence between operator& and operator<< is critical here.
102
810
  void operator&(std::ostream&) {}
103
};
104
105
// CVC5_FATAL() always aborts a function and provides a convenient way of
106
// formatting error messages. This can be used instead of a return type.
107
//
108
// Example function that returns a type Foo:
109
//   Foo bar(T t) {
110
//     switch(t.type()) {
111
//     ...
112
//     default:
113
//       CVC5_FATAL() << "Unknown T type " << t.enum();
114
//     }
115
//   }
116
#define CVC5_FATAL() \
117
  FatalStream(__PRETTY_FUNCTION__, __FILE__, __LINE__).stream()
118
119
/* GCC <= 9.2 ignores CVC5_NO_RETURN of ~FatalStream() if
120
 * used in template classes (e.g., CDHashMap::save()).  As a workaround we
121
 * explicitly call abort() to let the compiler know that the
122
 * corresponding function call will not return. */
123
#define SuppressWrongNoReturnWarning abort()
124
125
// If `cond` is true, log an error message and abort the process.
126
// Otherwise, does nothing. This leaves a hanging std::ostream& that can be
127
// inserted into.
128
#define CVC5_FATAL_IF(cond, function, file, line) \
129
  CVC5_PREDICT_FALSE(!(cond))                     \
130
  ? (void)0 : OstreamVoider() & FatalStream(function, file, line).stream()
131
132
// If `cond` is false, log an error message and abort()'s the process.
133
// Otherwise, does nothing. This leaves a hanging std::ostream& that can be
134
// inserted into using operator<<. Example usages:
135
//   AlwaysAssert(x >= 0);
136
//   AlwaysAssert(x >= 0) << "x must be positive";
137
//   AlwaysAssert(x >= 0) << "expected a positive value. Got " << x << "
138
//   instead";
139
#define AlwaysAssert(cond)                                        \
140
  CVC5_FATAL_IF(!(cond), __PRETTY_FUNCTION__, __FILE__, __LINE__) \
141
      << "Check failure\n\n " << #cond << "\n"
142
143
// Assert is a variant of AlwaysAssert() that is only checked when
144
// CVC5_ASSERTIONS is defined. We rely on the optimizer to remove the deadcode.
145
#ifdef CVC5_ASSERTIONS
146
#define Assert(cond) AlwaysAssert(cond)
147
#else
148
#define Assert(cond) \
149
  CVC5_FATAL_IF(false, __PRETTY_FUNCTION__, __FILE__, __LINE__)
150
#endif
151
152
14
class AssertArgumentException : public Exception
153
{
154
 protected:
155
  AssertArgumentException() : Exception() {}
156
157
  void construct(const char* header,
158
                 const char* extra,
159
                 const char* function,
160
                 const char* file,
161
                 unsigned line,
162
                 const char* fmt,
163
                 va_list args);
164
165
  void construct(const char* header,
166
                 const char* extra,
167
                 const char* function,
168
                 const char* file,
169
                 unsigned line);
170
171
 public:
172
  AssertArgumentException(const char* condStr,
173
                          const char* argDesc,
174
                          const char* function,
175
                          const char* file,
176
                          unsigned line,
177
                          const char* fmt,
178
                          ...);
179
180
  AssertArgumentException(const char* condStr,
181
                          const char* argDesc,
182
                          const char* function,
183
                          const char* file,
184
                          unsigned line);
185
186
}; /* class AssertArgumentException */
187
188
#define Unreachable() CVC5_FATAL() << "Unreachable code reached "
189
190
#define Unhandled() CVC5_FATAL() << "Unhandled case encountered "
191
192
#define Unimplemented() CVC5_FATAL() << "Unimplemented code encountered "
193
194
#define InternalError() CVC5_FATAL() << "Internal error detected "
195
196
#define IllegalArgument(arg, msg...)      \
197
  throw ::cvc5::IllegalArgumentException( \
198
      "",                                 \
199
      #arg,                               \
200
      __PRETTY_FUNCTION__,                \
201
      ::cvc5::IllegalArgumentException::formatVariadic(msg).c_str());
202
// This cannot use check argument directly as this forces
203
// CheckArgument to use a va_list. This is unsupported in Swig.
204
#define PrettyCheckArgument(cond, arg, msg...)                            \
205
  do                                                                      \
206
  {                                                                       \
207
    if (__builtin_expect((!(cond)), false))                               \
208
    {                                                                     \
209
      throw ::cvc5::IllegalArgumentException(                             \
210
          #cond,                                                          \
211
          #arg,                                                           \
212
          __PRETTY_FUNCTION__,                                            \
213
          ::cvc5::IllegalArgumentException::formatVariadic(msg).c_str()); \
214
    }                                                                     \
215
  } while (0)
216
#define AlwaysAssertArgument(cond, arg, msg...)                         \
217
  do                                                                    \
218
  {                                                                     \
219
    if (__builtin_expect((!(cond)), false))                             \
220
    {                                                                   \
221
      throw ::cvc5::AssertArgumentException(                            \
222
          #cond, #arg, __PRETTY_FUNCTION__, __FILE__, __LINE__, ##msg); \
223
    }                                                                   \
224
  } while (0)
225
226
#ifdef CVC5_ASSERTIONS
227
#define AssertArgument(cond, arg, msg...) AlwaysAssertArgument(cond, arg, ##msg)
228
#define DebugCheckArgument(cond, arg, msg...) CheckArgument(cond, arg, ##msg)
229
#else                                     /* ! CVC5_ASSERTIONS */
230
#define AssertArgument(cond, arg, msg...) /*__builtin_expect( ( cond ), true \
231
                                             )*/
232
#define DebugCheckArgument( \
233
    cond, arg, msg...) /*__builtin_expect( ( cond ), true )*/
234
#endif                 /* CVC5_ASSERTIONS */
235
236
}  // namespace cvc5
237
238
#endif /* CVC5__CHECK_H */