From: Tim King Date: Wed, 7 Feb 2018 18:19:04 +0000 (-0800) Subject: Adds a new CHECK macro that abort()s on failure. (#1532) X-Git-Tag: cvc5-1.0.0~5316 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=a5c952d63bca9f94d3886db4d9c09d08d7a23033;p=cvc5.git Adds a new CHECK macro that abort()s on failure. (#1532) --- diff --git a/src/base/Makefile.am b/src/base/Makefile.am index 5537bbbdd..7dd6f47e5 100644 --- a/src/base/Makefile.am +++ b/src/base/Makefile.am @@ -20,6 +20,8 @@ libbase_la_SOURCES = \ configuration_private.h \ cvc4_assert.cpp \ cvc4_assert.h \ + cvc4_check.cpp \ + cvc4_check.h \ exception.cpp \ exception.h \ listener.cpp \ diff --git a/src/base/cvc4_check.cpp b/src/base/cvc4_check.cpp new file mode 100644 index 000000000..5976ac3f7 --- /dev/null +++ b/src/base/cvc4_check.cpp @@ -0,0 +1,44 @@ +/********************* */ +/*! \file cvc4_check.cpp + ** \verbatim + ** Top contributors (to current version): + ** Tim King + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2018 by the authors listed in the file AUTHORS + ** in the top-level source directory) and their institutional affiliations. + ** All rights reserved. See the file COPYING in the top-level source + ** directory for licensing information.\endverbatim + ** + ** \brief Assertion utility classes, functions and macros. + ** + ** Implementation of assertion utility classes, functions and macros. + **/ + +#include "base/cvc4_check.h" + +#include +#include + +namespace CVC4 { + +FatalStream::FatalStream(const char* function, const char* file, int line) +{ + stream() << "Fatal failure within " << function << " at " << file << ":" + << line << "\n"; +} + +FatalStream::~FatalStream() +{ + Flush(); + abort(); +} + +std::ostream& FatalStream::stream() { return std::cerr; } + +void FatalStream::Flush() +{ + stream() << std::endl; + stream().flush(); +} + +} // namespace CVC4 diff --git a/src/base/cvc4_check.h b/src/base/cvc4_check.h new file mode 100644 index 000000000..fb4ec0bba --- /dev/null +++ b/src/base/cvc4_check.h @@ -0,0 +1,144 @@ +/********************* */ +/*! \file cvc4_check.h + ** \verbatim + ** Top contributors (to current version): + ** Tim King + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2018 by the authors listed in the file AUTHORS + ** in the top-level source directory) and their institutional affiliations. + ** All rights reserved. See the file COPYING in the top-level source + ** directory for licensing information.\endverbatim + ** + ** \brief Assertion utility classes, functions and macros. + ** + ** The CHECK utility classes, functions and macros are related to the Assert() + ** macros defined in base/cvc4_assert.h. The major distinguishing attribute + ** is the CHECK's abort() the process on failures while Assert() statements + ** throw C++ exceptions. + ** + ** The main usage in the file is the CHECK macros. The CHECK macros assert a + ** condition and aborts()'s the process if the condition is not satisfied. The + ** macro leaves a hanging ostream for the user to specify additional + ** information about the failure. Example usage: + ** CHECK(x >= 0) << "x must be positive."; + ** + ** DCHECK is a CHECK that is only enabled in debug builds. + ** DCHECK(pointer != nullptr); + ** + ** CVC4_FATAL() can be used to indicate unreachable code. + ** + ** The CHECK and DCHECK macros are not safe for use in signal-handling code. + ** TODO(taking): Add a signal-handling safe version of CHECK. + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__CHECK_H +#define __CVC4__CHECK_H + +#include + +// Define CVC4_NO_RETURN macro replacement for [[noreturn]]. +#if defined(SWIG) +#define CVC4_NO_RETURN +// SWIG does not yet support [[noreturn]] so emit nothing instead. +#else +#define CVC4_NO_RETURN [[noreturn]] +// Not checking for whether the compiler supports [[noreturn]] using +// __has_cpp_attribute as GCC 4.8 is too widespread and does not support this. +// We instead assume this is C++11 (or later) and [[noreturn]] is available. +#endif // defined(SWIG) + +// Define CVC4_PREDICT_FALSE(x) that helps the compiler predict that x will be +// false (if there is compiler support). +#ifdef __has_builtin +#if __has_builtin(__builtin_expect) +#define CVC4_PREDICT_FALSE(x) (__builtin_expect(x, false)) +#else +#define CVC4_PREDICT_FALSE(x) x +#endif +#else +#define CVC4_PREDICT_FALSE(x) x +#endif + +namespace CVC4 { + +// Implementation notes: +// To understand FatalStream and OStreamVoider, it is useful to understand +// how a CHECK is structured. CHECK(cond) is roughly the following pattern: +// cond ? (void)0 : OstreamVoider() & FatalStream().stream() +// This is a carefully crafted message to achieve a hanging ostream using +// operator precedence. The line `CHECK(cond) << foo << bar;` will bind as +// follows: +// `cond ? ((void)0) : (OSV() & ((FS().stream() << foo) << bar));` +// Once the expression is evaluated, the destructor ~FatalStream() of the +// temporary object is then run, which abort()'s the process. The role of the +// OStreamVoider() is to match the void type of the true branch. + +// Class that provides an ostream and whose destructor aborts! Direct usage of +// this class is discouraged. +class FatalStream +{ + public: + FatalStream(const char* function, const char* file, int line); + CVC4_NO_RETURN ~FatalStream(); + + std::ostream& stream(); + + private: + void Flush(); +}; + +// Helper class that changes the type of an std::ostream& into a void. See +// "Implementation notes" for more information. +class OstreamVoider +{ + public: + OstreamVoider() {} + // The operator precedence between operator& and operator<< is critical here. + void operator&(std::ostream&) {} +}; + +// CVC4_FATAL() always aborts a function and provides a convenient way of +// formatting error messages. This can be used instead of a return type. +// +// Example function that returns a type Foo: +// Foo bar(T t) { +// switch(t.type()) { +// ... +// default: +// CVC4_FATAL() << "Unknown T type " << t.enum(); +// } +// } +#define CVC4_FATAL() \ + FatalStream(__PRETTY_FUNCTION__, __FILE__, __LINE__).stream() + +// If `cond` is true, log an error message and abort the process. +// Otherwise, does nothing. This leaves a hanging std::ostream& that can be +// inserted into. +#define CVC4_FATAL_IF(cond, function, file, line) \ + CVC4_PREDICT_FALSE(!(cond)) \ + ? (void)0 : OstreamVoider() & FatalStream(function, file, line).stream() + +// If `cond` is false, log an error message and abort()'s the process. +// Otherwise, does nothing. This leaves a hanging std::ostream& that can be +// inserted into using operator<<. Example usages: +// CHECK(x >= 0); +// CHECK(x >= 0) << "x must be positive"; +// CHECK(x >= 0) << "expected a positive value. Got " << x << " instead"; +#define CHECK(cond) \ + CVC4_FATAL_IF(!(cond), __PRETTY_FUNCTION__, __FILE__, __LINE__) \ + << "Check failure\n\n " << #cond << "\n" + +// DCHECK is a variant of CHECK() that is only checked when CVC4_ASSERTIONS is +// defined. We rely on the optimizer to remove the deadcode. +#ifdef CVC4_ASSERTIONS +#define DCHECK(cond) CHECK(cond) +#else +#define DCHECK(cond) \ + CVC4_FATAL_IF(false, __PRETTY_FUNCTION__, __FILE__, __LINE__) +#endif /* CVC4_DEBUG */ + +} // namespace CVC4 + +#endif /* __CVC4__CHECK_H */ diff --git a/src/theory/bv/aig_bitblaster.cpp b/src/theory/bv/aig_bitblaster.cpp index 5459340f6..010eaf4e5 100644 --- a/src/theory/bv/aig_bitblaster.cpp +++ b/src/theory/bv/aig_bitblaster.cpp @@ -15,7 +15,10 @@ **/ #include "bitblaster_template.h" + #include "cvc4_private.h" + +#include "base/cvc4_check.h" #include "options/bv_options.h" #include "prop/cnf_stream.h" #include "prop/sat_solver_factory.h" @@ -155,8 +158,7 @@ AigBitblaster::AigBitblaster() d_satSolver = prop::SatSolverFactory::createCryptoMinisat(smtStatisticsRegistry(), "AigBitblaster"); break; - default: - Unreachable("Unknown SAT solver type"); + default: CVC4_FATAL() << "Unknown SAT solver type"; } } diff --git a/src/util/statistics_registry.cpp b/src/util/statistics_registry.cpp index 38113c512..2dd1eddd7 100644 --- a/src/util/statistics_registry.cpp +++ b/src/util/statistics_registry.cpp @@ -21,6 +21,7 @@ #include #include "base/cvc4_assert.h" +#include "base/cvc4_check.h" #include "lib/clock_gettime.h" #include "util/ostream_util.h" @@ -76,7 +77,7 @@ inline timespec& operator-=(timespec& a, const timespec& b) { nsec -= nsec_per_sec; ++a.tv_sec; } - Assert(nsec >= 0 && nsec < nsec_per_sec); + DCHECK(nsec >= 0 && nsec < nsec_per_sec); a.tv_nsec = nsec; return a; } @@ -167,22 +168,11 @@ void StatisticsRegistry::registerStat(Stat* s) void StatisticsRegistry::unregisterStat(Stat* s) { #ifdef CVC4_STATISTICS_ON - try - { - PrettyCheckArgument(d_stats.find(s) != d_stats.end(), - s, - "Statistic `%s' was not registered with this registry.", - s->getName().c_str()); - } - catch (Exception& e) - { - std::cerr << "Failure in StatisticsRegistry::unregisterStat():" << e.what() - << std::endl; - abort(); - } - d_stats.erase(s); + CHECK(s != nullptr); + CHECK(d_stats.erase(s) > 0) << "Statistic `" << s->getName() + << "' was not registered with this registry."; #endif /* CVC4_STATISTICS_ON */ -}/* StatisticsRegistry::unregisterStat_() */ +} /* StatisticsRegistry::unregisterStat() */ void StatisticsRegistry::flushStat(std::ostream &out) const { #ifdef CVC4_STATISTICS_ON @@ -212,16 +202,7 @@ void TimerStat::start() { void TimerStat::stop() { if(__CVC4_USE_STATISTICS) { - try - { - PrettyCheckArgument(d_running, *this, "timer not running"); - } - catch (Exception& e) - { - std::cerr << "Fatal failure in TimerStat::stop(): " << e.what() - << std::endl; - abort(); - } + CHECK(d_running) << "timer not running"; ::timespec end; clock_gettime(CLOCK_MONOTONIC, &end); d_data += end - d_start; diff --git a/test/unit/Makefile.am b/test/unit/Makefile.am index 9f61ef031..167100ff0 100644 --- a/test/unit/Makefile.am +++ b/test/unit/Makefile.am @@ -41,6 +41,7 @@ UNIT_TESTS += \ context/cdvector_black \ util/array_store_all_black \ util/assert_white \ + util/check_white \ util/binary_heap_black \ util/bitvector_black \ util/datatype_black \ diff --git a/test/unit/util/check_white.h b/test/unit/util/check_white.h new file mode 100644 index 000000000..e57afa6c7 --- /dev/null +++ b/test/unit/util/check_white.h @@ -0,0 +1,58 @@ +/********************* */ +/*! \file check_white.h + ** \verbatim + ** Top contributors (to current version): + ** Tim King + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS + ** in the top-level source directory) and their institutional affiliations. + ** All rights reserved. See the file COPYING in the top-level source + ** directory for licensing information.\endverbatim + ** + ** \brief White box testing of check utilities. + ** + ** White box testing of check utilities. + **/ + +#include + +#include +#include + +#include "base/cvc4_check.h" + +using namespace std; +using namespace CVC4; + +namespace { + +class CheckWhite : public CxxTest::TestSuite +{ + public: + const int kOne = 1; + + // This test just checks that this statement compiles. + std::string TerminalCvc4Fatal() const + { + CVC4_FATAL() << "This is a test that confirms that CVC4_FATAL can be a " + "terminal statement in a function that has a non-void " + "return type."; + } + + void testCheck() { CHECK(kOne >= 0) << kOne << " must be positive"; } + void testDCheck() + { + DCHECK(kOne == 1) << "always passes"; +#ifndef CVC4_ASSERTIONS + DCHECK(false) << "Will not be compiled in when CVC4_ASSERTIONS off."; +#endif /* CVC4_ASSERTIONS */ + } + + void testPointerTypeCanBeTheCondition() + { + const int* one_pointer = &kOne; + CHECK(one_pointer); + } +}; + +} // namespace