From a158abd5b6e0fa61cad4a5d1c80bbe8aa378f8b8 Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Mon, 19 Dec 2011 16:58:30 -0500 Subject: [PATCH] Bug 712129 - Implement MOZ_STATIC_ASSERT and MOZ_STATIC_ASSERT_IF. r=luke --- js/public/Utility.h | 29 +------------- mfbt/Assertions.h | 81 +++++++++++++++++++++++++++++++++++++++ xpcom/ds/nsAtomTable.cpp | 23 ++++++----- xpcom/glue/nsTArray-inl.h | 15 ++++---- xpcom/glue/nsTArray.h | 16 +++++--- 5 files changed, 114 insertions(+), 50 deletions(-) diff --git a/js/public/Utility.h b/js/public/Utility.h index 10614b3f5845..29f3d520ce63 100644 --- a/js/public/Utility.h +++ b/js/public/Utility.h @@ -89,33 +89,8 @@ JS_BEGIN_EXTERN_C # define JS_THREADSAFE_ASSERT(expr) ((void) 0) #endif -/* - * JS_STATIC_ASSERT - * - * A compile-time assert. "cond" must be a constant expression. The macro can - * be used only in places where an "extern" declaration is allowed. - */ -#ifdef __SUNPRO_CC -/* - * Sun Studio C++ compiler has a bug - * "sizeof expression not accepted as size of array parameter" - * It happens when js_static_assert() function is declared inside functions. - * The bug number is 6688515. It is not public yet. - * Therefore, for Sun Studio, declare js_static_assert as an array instead. - */ -# define JS_STATIC_ASSERT(cond) extern char js_static_assert[(cond) ? 1 : -1] -#else -# ifdef __COUNTER__ -# define JS_STATIC_ASSERT_GLUE1(x,y) x##y -# define JS_STATIC_ASSERT_GLUE(x,y) JS_STATIC_ASSERT_GLUE1(x,y) -# define JS_STATIC_ASSERT(cond) \ - typedef int JS_STATIC_ASSERT_GLUE(js_static_assert, __COUNTER__)[(cond) ? 1 : -1] -# else -# define JS_STATIC_ASSERT(cond) extern void js_static_assert(int arg[(cond) ? 1 : -1]) -# endif -#endif - -#define JS_STATIC_ASSERT_IF(cond, expr) JS_STATIC_ASSERT(!(cond) || (expr)) +#define JS_STATIC_ASSERT(cond) MOZ_STATIC_ASSERT(cond, "JS_STATIC_ASSERT") +#define JS_STATIC_ASSERT_IF(cond, expr) MOZ_STATIC_ASSERT_IF(cond, expr, "JS_STATIC_ASSERT_IF") /* * Abort the process in a non-graceful manner. This will cause a core file, diff --git a/mfbt/Assertions.h b/mfbt/Assertions.h index 6044a6bf0181..b21d7addfb07 100644 --- a/mfbt/Assertions.h +++ b/mfbt/Assertions.h @@ -43,6 +43,87 @@ #include "mozilla/Types.h" +/* + * MOZ_STATIC_ASSERT may be used to assert a condition *at compile time*. This + * can be useful when you make certain assumptions about what must hold for + * optimal, or even correct, behavior. For example, you might assert that the + * size of a struct is a multiple of the target architecture's word size: + * + * struct S { ... }; + * MOZ_STATIC_ASSERT(sizeof(S) % sizeof(size_t) == 0, + * "S should be a multiple of word size for efficiency"); + * + * This macro can be used in any location where both an extern declaration and a + * typedef could be used. + * + * Be aware of the gcc 4.2 concerns noted further down when writing patches that + * use this macro, particularly if a patch only bounces on OS X. + */ +#ifdef __cplusplus +# if defined(__clang__) +# ifndef __has_extension +# define __has_extension __has_feature /* compatibility, for older versions of clang */ +# endif +# if __has_extension(cxx_static_assert) +# define MOZ_STATIC_ASSERT(cond, reason) static_assert((cond), reason) +# endif +# elif defined(__GNUC__) +# if (defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L) && \ + (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) +# define MOZ_STATIC_ASSERT(cond, reason) static_assert((cond), #cond) +# endif +# elif defined(_MSC_VER) +# if _MSC_VER >= 1600 /* MSVC 10 */ +# define MOZ_STATIC_ASSERT(cond, reason) static_assert((cond), #cond) +# endif +# elif defined(__HP_aCC) +# if __HP_aCC >= 62500 && defined(_HP_CXX0x_SOURCE) +# define MOZ_STATIC_ASSERT(cond, reason) static_assert((cond), #cond) +# endif +# endif +#endif +#ifndef MOZ_STATIC_ASSERT +# define MOZ_STATIC_ASSERT_GLUE1(x, y) x##y +# define MOZ_STATIC_ASSERT_GLUE(x, y) MOZ_STATIC_ASSERT_GLUE1(x, y) +# if defined(__SUNPRO_CC) + /* + * The Sun Studio C++ compiler is buggy when declaring, inside a function, + * another extern'd function with an array argument whose length contains a + * sizeof, triggering the error message "sizeof expression not accepted as + * size of array parameter". This bug (6688515, not public yet) would hit + * defining moz_static_assert as a function, so we always define an extern + * array for Sun Studio. + * + * We include the line number in the symbol name in a best-effort attempt + * to avoid conflicts (see below). + */ +# define MOZ_STATIC_ASSERT(cond, reason) \ + extern char MOZ_STATIC_ASSERT_GLUE(moz_static_assert, __LINE__)[(cond) ? 1 : -1] +# elif defined(__COUNTER__) + /* + * If there was no preferred alternative, use a compiler-agnostic version. + * + * Note that the non-__COUNTER__ version has a bug in C++: it can't be used + * in both |extern "C"| and normal C++ in the same translation unit. (Alas + * |extern "C"| isn't allowed in a function.) The only affected compiler + * we really care about is gcc 4.2. For that compiler and others like it, + * we include the line number in the function name to do the best we can to + * avoid conflicts. These should be rare: a conflict would require use of + * MOZ_STATIC_ASSERT on the same line in separate files in the same + * translation unit, *and* the uses would have to be in code with + * different linkage, *and* the first observed use must be in C++-linkage + * code. + */ +# define MOZ_STATIC_ASSERT(cond, reason) \ + typedef int MOZ_STATIC_ASSERT_GLUE(moz_static_assert, __COUNTER__)[(cond) ? 1 : -1] +# else +# define MOZ_STATIC_ASSERT(cond, reason) \ + extern void MOZ_STATIC_ASSERT_GLUE(moz_static_assert, __LINE__)(int arg[(cond) ? 1 : -1]) +# endif +#endif + +#define MOZ_STATIC_ASSERT_IF(cond, expr, reason) MOZ_STATIC_ASSERT(!(cond) || (expr), reason) + /* * XXX: we're cheating here in order to avoid creating object files * for mfbt /just/ to provide a function like FatalError() to be used diff --git a/xpcom/ds/nsAtomTable.cpp b/xpcom/ds/nsAtomTable.cpp index 8d6e88e45ddc..6706519e7655 100644 --- a/xpcom/ds/nsAtomTable.cpp +++ b/xpcom/ds/nsAtomTable.cpp @@ -36,6 +36,8 @@ * * ***** END LICENSE BLOCK ***** */ +#include "mozilla/Assertions.h" + #include "nsAtomTable.h" #include "nsStaticAtom.h" #include "nsString.h" @@ -406,16 +408,17 @@ GetAtomHashEntry(const PRUnichar* aString, PRUint32 aLength) class CheckStaticAtomSizes { CheckStaticAtomSizes() { - PR_STATIC_ASSERT((sizeof(nsFakeStringBuffer<1>().mRefCnt) == - sizeof(nsStringBuffer().mRefCount)) && - (sizeof(nsFakeStringBuffer<1>().mSize) == - sizeof(nsStringBuffer().mStorageSize)) && - (offsetof(nsFakeStringBuffer<1>, mRefCnt) == - offsetof(nsStringBuffer, mRefCount)) && - (offsetof(nsFakeStringBuffer<1>, mSize) == - offsetof(nsStringBuffer, mStorageSize)) && - (offsetof(nsFakeStringBuffer<1>, mStringData) == - sizeof(nsStringBuffer))); + MOZ_STATIC_ASSERT((sizeof(nsFakeStringBuffer<1>().mRefCnt) == + sizeof(nsStringBuffer().mRefCount)) && + (sizeof(nsFakeStringBuffer<1>().mSize) == + sizeof(nsStringBuffer().mStorageSize)) && + (offsetof(nsFakeStringBuffer<1>, mRefCnt) == + offsetof(nsStringBuffer, mRefCount)) && + (offsetof(nsFakeStringBuffer<1>, mSize) == + offsetof(nsStringBuffer, mStorageSize)) && + (offsetof(nsFakeStringBuffer<1>, mStringData) == + sizeof(nsStringBuffer)), + "mocked-up strings' representations should be compatible"); } }; diff --git a/xpcom/glue/nsTArray-inl.h b/xpcom/glue/nsTArray-inl.h index 8ccb23c1f359..5a0a842721d0 100644 --- a/xpcom/glue/nsTArray-inl.h +++ b/xpcom/glue/nsTArray-inl.h @@ -64,12 +64,12 @@ const nsTArrayHeader* nsTArray_base::GetAutoArrayBufferUnsafe(size_t elem // If we're on a 32-bit system and elemAlign is 8, we need to adjust our // pointer to take into account the extra alignment in the auto array. - // Check that the auto array is padded as we expect. - PR_STATIC_ASSERT(sizeof(void*) != 4 || - (MOZ_ALIGNOF(mozilla::AlignedElem<8>) == 8 && - sizeof(nsAutoTArray, 1>) == - sizeof(void*) + sizeof(nsTArrayHeader) + - 4 + sizeof(mozilla::AlignedElem<8>))); + MOZ_STATIC_ASSERT(sizeof(void*) != 4 || + (MOZ_ALIGNOF(mozilla::AlignedElem<8>) == 8 && + sizeof(nsAutoTArray, 1>) == + sizeof(void*) + sizeof(nsTArrayHeader) + + 4 + sizeof(mozilla::AlignedElem<8>)), + "auto array padding wasn't what we expected"); // We don't support alignments greater than 8 bytes. NS_ABORT_IF_FALSE(elemAlign <= 4 || elemAlign == 8, "unsupported alignment."); @@ -115,7 +115,8 @@ bool nsTArray_base::UsesAutoArrayBuffer() const { // owned by this nsAutoTArray. We statically assert that elem_type's // alignment is 8 bytes or less in nsAutoArrayBase. - PR_STATIC_ASSERT(sizeof(nsTArrayHeader) > 4); + MOZ_STATIC_ASSERT(sizeof(nsTArrayHeader) > 4, + "see comment above"); #ifdef DEBUG PRPtrdiff diff = reinterpret_cast(GetAutoArrayBuffer(8)) - diff --git a/xpcom/glue/nsTArray.h b/xpcom/glue/nsTArray.h index 06b3ee980bfb..cc538e599e25 100644 --- a/xpcom/glue/nsTArray.h +++ b/xpcom/glue/nsTArray.h @@ -39,6 +39,9 @@ #ifndef nsTArray_h__ #define nsTArray_h__ +#include "mozilla/Assertions.h" +#include "mozilla/Util.h" + #include #include "prtypes.h" @@ -47,7 +50,6 @@ #include "nsQuickSort.h" #include "nsDebug.h" #include "nsTraceRefcnt.h" -#include "mozilla/Util.h" #include NEW_H // @@ -1316,9 +1318,9 @@ private: friend class nsTArray_base; void Init() { - // We can't handle alignments greater than 8; see - // nsTArray_base::UsesAutoArrayBuffer(). - PR_STATIC_ASSERT(MOZ_ALIGNOF(elem_type) <= 8); + MOZ_STATIC_ASSERT(MOZ_ALIGNOF(elem_type) <= 8, + "can't handle alignments greater than 8, " + "see nsTArray_base::UsesAutoArrayBuffer()"); *base_type::PtrToHdr() = reinterpret_cast(&mAutoBuf); base_type::Hdr()->mLength = 0; @@ -1367,8 +1369,10 @@ public: // 64-bit system, where the compiler inserts 4 bytes of padding at the end of // the auto array to make its size a multiple of alignof(void*) == 8 bytes. -PR_STATIC_ASSERT(sizeof(nsAutoTArray) == - sizeof(void*) + sizeof(nsTArrayHeader) + sizeof(PRUint32) * 2); +MOZ_STATIC_ASSERT(sizeof(nsAutoTArray) == + sizeof(void*) + sizeof(nsTArrayHeader) + sizeof(PRUint32) * 2, + "nsAutoTArray shouldn't contain any extra padding, " + "see the comment"); template class AutoFallibleTArray : public nsAutoArrayBase, N>