Logging: mark qt_assert()/qt_assert_x()/qFatal() as nothrow

These functions are not supposed to return, not even by exception.
qt_message() _can_ throw, but we're fine with the compiler calling
std::terminate() then, since the backtrace will still include the
assertion location.

This behaviour is ensured by a new macro, QT_TERMINATE_ON_EXCEPTION,
which expands to something like
   try { expr; } catch(...) { std::terminate(); }
if the compiler doesn't support Q_DECL_NOEXCEPT (but maybe
Q_DECL_NOTHROW), and to something like just
   expr;
otherwise (including in the QT_NO_EXCEPTION case).

The real macro preserves scopes in all cases, and aims
to work even if <exception> isn't included in the TU it's used in,
so is a little bit more complex than that.

Change-Id: Ie6a2b7776e6aa77e57bd9aea6e184e5fa1cec81c
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Marc Mutz 2012-08-03 10:38:00 +02:00 committed by Qt by Nokia
parent 7ef395224e
commit c856e37c5f
4 changed files with 55 additions and 7 deletions

View File

@ -1913,10 +1913,19 @@ void qBadAlloc()
QT_THROW(std::bad_alloc());
}
/* \internal
Allows you to call std::terminate() without including <exception>.
Called internally from QT_TERMINATE_ON_EXCEPTION
*/
Q_NORETURN void qTerminate() Q_DECL_NOTHROW
{
std::terminate();
}
/*
The Q_ASSERT macro calls this function when the test fails.
*/
void qt_assert(const char *assertion, const char *file, int line)
void qt_assert(const char *assertion, const char *file, int line) Q_DECL_NOTHROW
{
qFatal("ASSERT: \"%s\" in file %s, line %d", assertion, file, line);
}
@ -1924,7 +1933,7 @@ void qt_assert(const char *assertion, const char *file, int line)
/*
The Q_ASSERT_X macro calls this function when the test fails.
*/
void qt_assert_x(const char *where, const char *what, const char *file, int line)
void qt_assert_x(const char *where, const char *what, const char *file, int line) Q_DECL_NOTHROW
{
qFatal("ASSERT failure in %s: \"%s\", file %s, line %d", where, what, file, line);
}
@ -3033,6 +3042,38 @@ bool QInternal::activateCallbacks(Callback cb, void **parameters)
\sa Q_DECL_NOEXCEPT, Q_DECL_NOEXCEPT_EXPR
*/
/*!
\macro QT_TERMINATE_ON_EXCEPTION(expr)
\relates <QtGlobal>
\internal
In general, use of the Q_DECL_NOEXCEPT macro is preferred over
Q_DECL_NOTHROW, because it exhibits well-defined behavior and
supports the more powerful Q_DECL_NOEXCEPT_EXPR variant. However,
use of Q_DECL_NOTHROW has the advantage that Windows builds
benefit on a wide range or compiler versions that do not yet
support the C++11 noexcept feature.
It may therefore be beneficial to use Q_DECL_NOTHROW and emulate
the C++11 behavior manually with an embedded try/catch.
Qt provides the QT_TERMINATE_ON_EXCEPTION(expr) macro for this
purpose. It either expands to \c expr (if Qt is compiled without
exception support or the compiler supports C++11 noexcept
semantics) or to
\code
try { expr; } catch(...) { qTerminate(); }
\endocde
otherwise.
Since this macro expands to just \c expr if the compiler supports
C++11 noexcept, expecting the compiler to take over responsibility
of calling std::terminate() in that case, it should not be used
outside Q_DECL_NOTHROW functions.
\sa Q_DECL_NOEXCEPT, Q_DECL_NOTHROW, qTerminate()
*/
/*!
\macro Q_DECL_NOEXCEPT
\relates <QtGlobal>

View File

@ -546,11 +546,18 @@ inline void qt_noop(void) {}
# define QT_CATCH(A) else
# define QT_THROW(A) qt_noop()
# define QT_RETHROW qt_noop()
# define QT_TERMINATE_ON_EXCEPTION(expr) do { expr; } while (0)
#else
# define QT_TRY try
# define QT_CATCH(A) catch (A)
# define QT_THROW(A) throw A
# define QT_RETHROW throw
Q_NORETURN Q_CORE_EXPORT void qTerminate() Q_DECL_NOTHROW;
# ifdef Q_COMPILER_NOEXCEPT
# define QT_TERMINATE_ON_EXCEPTION(expr) do { expr; } while (0)
# else
# define QT_TERMINATE_ON_EXCEPTION(expr) do { try { expr; } catch (...) { qTerminate(); } } while (0)
# endif
#endif
Q_CORE_EXPORT const char *qVersion();
@ -593,7 +600,7 @@ Q_CORE_EXPORT QString qt_error_string(int errorCode = -1);
#ifndef Q_CC_MSVC
Q_NORETURN
#endif
Q_CORE_EXPORT void qt_assert(const char *assertion, const char *file, int line);
Q_CORE_EXPORT void qt_assert(const char *assertion, const char *file, int line) Q_DECL_NOTHROW;
#if !defined(Q_ASSERT)
# if defined(QT_NO_DEBUG) && !defined(QT_FORCE_ASSERTS)
@ -610,7 +617,7 @@ Q_CORE_EXPORT void qt_assert(const char *assertion, const char *file, int line);
#ifndef Q_CC_MSVC
Q_NORETURN
#endif
Q_CORE_EXPORT void qt_assert_x(const char *where, const char *what, const char *file, int line);
Q_CORE_EXPORT void qt_assert_x(const char *where, const char *what, const char *file, int line) Q_DECL_NOTHROW;
#if !defined(Q_ASSERT_X)
# if defined(QT_NO_DEBUG) && !defined(QT_FORCE_ASSERTS)

View File

@ -240,11 +240,11 @@ QDebug QMessageLogger::critical()
#endif
#undef qFatal
void QMessageLogger::fatal(const char *msg, ...)
void QMessageLogger::fatal(const char *msg, ...) Q_DECL_NOTHROW
{
va_list ap;
va_start(ap, msg); // use variable arg list
qt_message(QtFatalMsg, context, msg, ap);
QT_TERMINATE_ON_EXCEPTION(qt_message(QtFatalMsg, context, msg, ap));
#ifndef Q_CC_MSVC
Q_UNREACHABLE();
#endif

View File

@ -117,7 +117,7 @@ public:
#ifndef Q_CC_MSVC
Q_NORETURN
#endif
void fatal(const char *msg, ...)
void fatal(const char *msg, ...) Q_DECL_NOTHROW
#if defined(Q_CC_GNU) && !defined(__INSURE__)
__attribute__ ((format (printf, 2, 3)))
#endif