From 0ac2dca977ecc4020f51af57908a2640d00bcd9e Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Sun, 5 Nov 2017 17:54:35 -0800 Subject: [PATCH] Add support for C11 static_assert and thread_local Tested with Clang, GCC 4.5 & up, ICC 17 and MSVC 2017. No current version of MSVC supports C11 and GCC implemented the features slightly later in C than in C++. Change-Id: I57a1bd6e0c194530b732fffd14f45c5074c9a052 Reviewed-by: Allan Sandfeld Jensen Reviewed-by: Olivier Goffart (Woboq GmbH) --- src/corelib/global/qcompilerdetection.h | 29 +++++ src/corelib/global/qglobal.h | 53 +++++---- tests/auto/corelib/global/qglobal/qglobal.c | 111 ++++++++++++++++++ tests/auto/corelib/global/qglobal/qglobal.pro | 2 +- .../corelib/global/qglobal/tst_qglobal.cpp | 17 +++ 5 files changed, 185 insertions(+), 27 deletions(-) create mode 100644 tests/auto/corelib/global/qglobal/qglobal.c diff --git a/src/corelib/global/qcompilerdetection.h b/src/corelib/global/qcompilerdetection.h index 2c58ff87e9..e4b756f4b7 100644 --- a/src/corelib/global/qcompilerdetection.h +++ b/src/corelib/global/qcompilerdetection.h @@ -651,6 +651,12 @@ # undef Q_COMPILER_CONSTEXPR # endif # endif +# elif defined(__STDC_VERSION__) && __STDC_VERSION__ > 199901L s +// C11 features supported. Only tested with ICC 17 and up. +# define Q_COMPILER_STATIC_ASSERT +# if __has_include() +# define Q_COMPILER_THREAD_LOCAL +# endif # endif #endif @@ -803,6 +809,17 @@ # endif # endif +# if defined(__STDC_VERSION__) +# if __has_feature(c_static_assert) +# define Q_COMPILER_STATIC_ASSERT +# endif +# if __has_feature(c_thread_local) && __has_include() +# if !defined(__FreeBSD__) /* FreeBSD clang fails on __cxa_thread_atexit */ +# define Q_COMPILER_THREAD_LOCAL +# endif +# endif +# endif + # if defined(__has_warning) # if __has_warning("-Wunused-private-field") # define Q_DECL_UNUSED_MEMBER Q_DECL_UNUSED @@ -898,6 +915,18 @@ # define Q_COMPILER_RETURN_TYPE_DEDUCTION # endif # endif +# if defined(__STDC_VERSION__) && __STDC_VERSION__ > 199901L +# if Q_CC_GNU >= 407 + /* C11 features supported in GCC 4.7: */ +# define Q_COMPILER_STATIC_ASSERT +# endif +# if Q_CC_GNU >= 409 + /* C11 features supported in GCC 4.9: */ +# if __has_include() +# define Q_COMPILER_THREAD_LOCAL +# endif +# endif +# endif #endif #if defined(Q_CC_MSVC) diff --git a/src/corelib/global/qglobal.h b/src/corelib/global/qglobal.h index e49bace002..33b86a7321 100644 --- a/src/corelib/global/qglobal.h +++ b/src/corelib/global/qglobal.h @@ -47,6 +47,7 @@ # include #endif #ifndef __ASSEMBLER__ +# include # include #endif @@ -105,6 +106,32 @@ # define Q_OF_MACH_O #endif +/* + Avoid "unused parameter" warnings +*/ +#define Q_UNUSED(x) (void)x; + +#if defined(__cplusplus) && defined(Q_COMPILER_STATIC_ASSERT) +# define Q_STATIC_ASSERT(Condition) static_assert(bool(Condition), #Condition) +# define Q_STATIC_ASSERT_X(Condition, Message) static_assert(bool(Condition), Message) +#elif defined(Q_COMPILER_STATIC_ASSERT) +// C11 mode - using the _S version in case doesn't do the right thing +# define Q_STATIC_ASSERT(Condition) _Static_assert(!!(Condition), #Condition) +# define Q_STATIC_ASSERT_X(Condition, Message) _Static_assert(!!(Condition), Message) +#else +// C89 & C99 version +# define Q_STATIC_ASSERT_PRIVATE_JOIN(A, B) Q_STATIC_ASSERT_PRIVATE_JOIN_IMPL(A, B) +# define Q_STATIC_ASSERT_PRIVATE_JOIN_IMPL(A, B) A ## B +# ifdef __COUNTER__ +# define Q_STATIC_ASSERT(Condition) \ + typedef char Q_STATIC_ASSERT_PRIVATE_JOIN(q_static_assert_result, __COUNTER__) [(Condition) ? 1 : -1]; +# else +# define Q_STATIC_ASSERT(Condition) \ + typedef char Q_STATIC_ASSERT_PRIVATE_JOIN(q_static_assert_result, __LINE__) [(Condition) ? 1 : -1]; +# endif /* __COUNTER__ */ +# define Q_STATIC_ASSERT_X(Condition, Message) Q_STATIC_ASSERT(Condition) +#endif + #ifdef __cplusplus #include @@ -687,11 +714,6 @@ Q_CORE_EXPORT Q_DECL_CONST_FUNCTION bool qSharedBuild() Q_DECL_NOTHROW; # define Q_INLINE_TEMPLATE inline #endif -/* - Avoid "unused parameter" warnings -*/ -#define Q_UNUSED(x) (void)x; - /* Debugging and error handling */ @@ -750,27 +772,6 @@ Q_CORE_EXPORT void qt_assert_x(const char *where, const char *what, const char * # endif #endif - -#ifdef Q_COMPILER_STATIC_ASSERT -#define Q_STATIC_ASSERT(Condition) static_assert(bool(Condition), #Condition) -#define Q_STATIC_ASSERT_X(Condition, Message) static_assert(bool(Condition), Message) -#else -// Intentionally undefined -template class QStaticAssertFailure; -template <> class QStaticAssertFailure {}; - -#define Q_STATIC_ASSERT_PRIVATE_JOIN(A, B) Q_STATIC_ASSERT_PRIVATE_JOIN_IMPL(A, B) -#define Q_STATIC_ASSERT_PRIVATE_JOIN_IMPL(A, B) A ## B -#ifdef __COUNTER__ -#define Q_STATIC_ASSERT(Condition) \ - enum {Q_STATIC_ASSERT_PRIVATE_JOIN(q_static_assert_result, __COUNTER__) = sizeof(QStaticAssertFailure)} -#else -#define Q_STATIC_ASSERT(Condition) \ - enum {Q_STATIC_ASSERT_PRIVATE_JOIN(q_static_assert_result, __LINE__) = sizeof(QStaticAssertFailure)} -#endif /* __COUNTER__ */ -#define Q_STATIC_ASSERT_X(Condition, Message) Q_STATIC_ASSERT(Condition) -#endif - Q_NORETURN Q_CORE_EXPORT void qt_check_pointer(const char *, int) Q_DECL_NOTHROW; Q_CORE_EXPORT void qBadAlloc(); diff --git a/tests/auto/corelib/global/qglobal/qglobal.c b/tests/auto/corelib/global/qglobal/qglobal.c new file mode 100644 index 0000000000..6a6f53dfe0 --- /dev/null +++ b/tests/auto/corelib/global/qglobal/qglobal.c @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Intel Corporation. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#if QT_HAS_INCLUDE() || __STDC_VERSION__ >= 199901L +# include +#else +# undef true +# define true 1 +# undef false +# define false 0 +#endif + +#ifdef Q_COMPILER_THREAD_LOCAL +# include +#endif + +/* + * Certain features of qglobal.h must work in C mode too. We test that + * everything works. + */ + +/* Types and Q_UNUSED */ +void tst_GlobalTypes() +{ + qint8 s8; + qint16 s16; + qint32 s32; + qint64 s64; + qlonglong sll; + Q_UNUSED(s8); Q_UNUSED(s16); Q_UNUSED(s32); Q_UNUSED(s64); Q_UNUSED(sll); + + quint8 u8; + quint16 u16; + quint32 u32; + quint64 u64; + qulonglong ull; + Q_UNUSED(u8); Q_UNUSED(u16); Q_UNUSED(u32); Q_UNUSED(u64); Q_UNUSED(ull); + + uchar uc; + ushort us; + uint ui; + ulong ul; + Q_UNUSED(uc); Q_UNUSED(us); Q_UNUSED(ui); Q_UNUSED(ul); + + qreal qr; + Q_UNUSED(qr); +} + +/* Qt version */ +int tst_QtVersion() +{ + return QT_VERSION; +} + +const char *tst_qVersion() Q_DECL_NOTHROW +{ +#if !defined(QT_NAMESPACE) + return qVersion(); +#else + return NULL; +#endif +} + +/* Static assertion */ +Q_STATIC_ASSERT(true); +Q_STATIC_ASSERT(1); +Q_STATIC_ASSERT_X(true, "Message"); +Q_STATIC_ASSERT_X(1, "Message"); + +Q_STATIC_ASSERT(!false); +Q_STATIC_ASSERT(!0); + +Q_STATIC_ASSERT(!!true); +Q_STATIC_ASSERT(!!1); + +#ifdef Q_COMPILER_THREAD_LOCAL +static thread_local int gt_var; +void thread_local_test() +{ + thread_local int t_var; + t_var = gt_var; +} +#endif + diff --git a/tests/auto/corelib/global/qglobal/qglobal.pro b/tests/auto/corelib/global/qglobal/qglobal.pro index b8ed7761f5..a40cb9a288 100644 --- a/tests/auto/corelib/global/qglobal/qglobal.pro +++ b/tests/auto/corelib/global/qglobal/qglobal.pro @@ -1,4 +1,4 @@ CONFIG += testcase TARGET = tst_qglobal QT = core testlib -SOURCES = tst_qglobal.cpp +SOURCES = tst_qglobal.cpp qglobal.c diff --git a/tests/auto/corelib/global/qglobal/tst_qglobal.cpp b/tests/auto/corelib/global/qglobal/tst_qglobal.cpp index 083526fdc4..78b954f373 100644 --- a/tests/auto/corelib/global/qglobal/tst_qglobal.cpp +++ b/tests/auto/corelib/global/qglobal/tst_qglobal.cpp @@ -39,6 +39,7 @@ class tst_QGlobal: public QObject Q_OBJECT private slots: + void cMode(); void qIsNull(); void for_each(); void qassert(); @@ -56,6 +57,22 @@ private slots: void testqOverload(); }; +extern "C" { // functions in qglobal.c +void tst_GlobalTypes(); +int tst_QtVersion(); +const char *tst_qVersion(); +} + +void tst_QGlobal::cMode() +{ + tst_GlobalTypes(); + QCOMPARE(tst_QtVersion(), QT_VERSION); + +#ifndef QT_NAMESPACE + QCOMPARE(tst_qVersion(), qVersion()); +#endif +} + void tst_QGlobal::qIsNull() { double d = 0.0;