GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/base/check.h Lines: 3 3 100.0 %
Date: 2021-09-15 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
namespace cvc5 {
43
44
// Implementation notes:
45
// To understand FatalStream and OStreamVoider, it is useful to understand
46
// how a AlwaysAssert is structured. AlwaysAssert(cond) is roughly the following
47
// pattern:
48
//  cond ? (void)0 : OstreamVoider() & FatalStream().stream()
49
// This is a carefully crafted message to achieve a hanging ostream using
50
// operator precedence. The line `AlwaysAssert(cond) << foo << bar;` will bind
51
// as follows:
52
//  `cond ? ((void)0) : (OSV() & ((FS().stream() << foo) << bar));`
53
// Once the expression is evaluated, the destructor ~FatalStream() of the
54
// temporary object is then run, which abort()'s the process. The role of the
55
// OStreamVoider() is to match the void type of the true branch.
56
57
// Class that provides an ostream and whose destructor aborts! Direct usage of
58
// this class is discouraged.
59
class CVC5_EXPORT FatalStream
60
{
61
 public:
62
  FatalStream(const char* function, const char* file, int line);
63
  [[noreturn]] ~FatalStream();
64
65
  std::ostream& stream();
66
67
 private:
68
  void Flush();
69
};
70
71
// Helper class that changes the type of an std::ostream& into a void. See
72
// "Implementation notes" for more information.
73
class OstreamVoider
74
{
75
 public:
76
842
  OstreamVoider() {}
77
  // The operator precedence between operator& and operator<< is critical here.
78
842
  void operator&(std::ostream&) {}
79
};
80
81
// CVC5_FATAL() always aborts a function and provides a convenient way of
82
// formatting error messages. This can be used instead of a return type.
83
//
84
// Example function that returns a type Foo:
85
//   Foo bar(T t) {
86
//     switch(t.type()) {
87
//     ...
88
//     default:
89
//       CVC5_FATAL() << "Unknown T type " << t.enum();
90
//     }
91
//   }
92
#define CVC5_FATAL() \
93
  FatalStream(__PRETTY_FUNCTION__, __FILE__, __LINE__).stream()
94
95
/* GCC <= 9.2 ignores CVC5_NO_RETURN of ~FatalStream() if
96
 * used in template classes (e.g., CDHashMap::save()).  As a workaround we
97
 * explicitly call abort() to let the compiler know that the
98
 * corresponding function call will not return. */
99
#define SuppressWrongNoReturnWarning abort()
100
101
// If `cond` is true, log an error message and abort the process.
102
// Otherwise, does nothing. This leaves a hanging std::ostream& that can be
103
// inserted into.
104
#define CVC5_FATAL_IF(cond, function, file, line) \
105
  CVC5_PREDICT_FALSE(!(cond))                     \
106
  ? (void)0 : OstreamVoider() & FatalStream(function, file, line).stream()
107
108
// If `cond` is false, log an error message and abort()'s the process.
109
// Otherwise, does nothing. This leaves a hanging std::ostream& that can be
110
// inserted into using operator<<. Example usages:
111
//   AlwaysAssert(x >= 0);
112
//   AlwaysAssert(x >= 0) << "x must be positive";
113
//   AlwaysAssert(x >= 0) << "expected a positive value. Got " << x << "
114
//   instead";
115
#define AlwaysAssert(cond)                                        \
116
  CVC5_FATAL_IF(!(cond), __PRETTY_FUNCTION__, __FILE__, __LINE__) \
117
      << "Check failure\n\n " << #cond << "\n"
118
119
// Assert is a variant of AlwaysAssert() that is only checked when
120
// CVC5_ASSERTIONS is defined. We rely on the optimizer to remove the deadcode.
121
#ifdef CVC5_ASSERTIONS
122
#define Assert(cond) AlwaysAssert(cond)
123
#else
124
#define Assert(cond) \
125
  CVC5_FATAL_IF(false, __PRETTY_FUNCTION__, __FILE__, __LINE__)
126
#endif
127
128
14
class AssertArgumentException : public Exception
129
{
130
 protected:
131
  AssertArgumentException() : Exception() {}
132
133
  void construct(const char* header,
134
                 const char* extra,
135
                 const char* function,
136
                 const char* file,
137
                 unsigned line,
138
                 const char* fmt,
139
                 va_list args);
140
141
  void construct(const char* header,
142
                 const char* extra,
143
                 const char* function,
144
                 const char* file,
145
                 unsigned line);
146
147
 public:
148
  AssertArgumentException(const char* condStr,
149
                          const char* argDesc,
150
                          const char* function,
151
                          const char* file,
152
                          unsigned line,
153
                          const char* fmt,
154
                          ...);
155
156
  AssertArgumentException(const char* condStr,
157
                          const char* argDesc,
158
                          const char* function,
159
                          const char* file,
160
                          unsigned line);
161
162
}; /* class AssertArgumentException */
163
164
#define Unreachable() CVC5_FATAL() << "Unreachable code reached "
165
166
#define Unhandled() CVC5_FATAL() << "Unhandled case encountered "
167
168
#define Unimplemented() CVC5_FATAL() << "Unimplemented code encountered "
169
170
#define InternalError() CVC5_FATAL() << "Internal error detected "
171
172
#define IllegalArgument(arg, msg...)      \
173
  throw ::cvc5::IllegalArgumentException( \
174
      "",                                 \
175
      #arg,                               \
176
      __PRETTY_FUNCTION__,                \
177
      ::cvc5::IllegalArgumentException::formatVariadic(msg).c_str());
178
// This cannot use check argument directly as this forces
179
// CheckArgument to use a va_list. This is unsupported in Swig.
180
#define PrettyCheckArgument(cond, arg, msg...)                            \
181
  do                                                                      \
182
  {                                                                       \
183
    if (__builtin_expect((!(cond)), false))                               \
184
    {                                                                     \
185
      throw ::cvc5::IllegalArgumentException(                             \
186
          #cond,                                                          \
187
          #arg,                                                           \
188
          __PRETTY_FUNCTION__,                                            \
189
          ::cvc5::IllegalArgumentException::formatVariadic(msg).c_str()); \
190
    }                                                                     \
191
  } while (0)
192
#define AlwaysAssertArgument(cond, arg, msg...)                         \
193
  do                                                                    \
194
  {                                                                     \
195
    if (__builtin_expect((!(cond)), false))                             \
196
    {                                                                   \
197
      throw ::cvc5::AssertArgumentException(                            \
198
          #cond, #arg, __PRETTY_FUNCTION__, __FILE__, __LINE__, ##msg); \
199
    }                                                                   \
200
  } while (0)
201
202
#ifdef CVC5_ASSERTIONS
203
#define AssertArgument(cond, arg, msg...) AlwaysAssertArgument(cond, arg, ##msg)
204
#define DebugCheckArgument(cond, arg, msg...) CheckArgument(cond, arg, ##msg)
205
#else                                     /* ! CVC5_ASSERTIONS */
206
#define AssertArgument(cond, arg, msg...) /*__builtin_expect( ( cond ), true \
207
                                             )*/
208
#define DebugCheckArgument( \
209
    cond, arg, msg...) /*__builtin_expect( ( cond ), true )*/
210
#endif                 /* CVC5_ASSERTIONS */
211
212
}  // namespace cvc5
213
214
#endif /* CVC5__CHECK_H */