Bug 712129 - Implement MOZ_STATIC_ASSERT and MOZ_STATIC_ASSERT_IF. r=luke
authorJeff Walden <jwalden@mit.edu>
Mon, 19 Dec 2011 16:58:30 -0500
changeset 85682 8d4a9617fcd105c8e06d80abfd4b0c3de47ff486
parent 85681 0d7567cf0a67a44906ab85ba9f2acc6ead70ba63
child 85683 d9e4c34ca1c603d61fe986249324090c84d0b146
push idunknown
push userunknown
push dateunknown
reviewersluke
bugs712129
milestone12.0a1
Bug 712129 - Implement MOZ_STATIC_ASSERT and MOZ_STATIC_ASSERT_IF. r=luke
js/public/Utility.h
mfbt/Assertions.h
xpcom/ds/nsAtomTable.cpp
xpcom/glue/nsTArray-inl.h
xpcom/glue/nsTArray.h
--- a/js/public/Utility.h
+++ b/js/public/Utility.h
@@ -84,43 +84,18 @@ JS_BEGIN_EXTERN_C
 #  define JS_THREADSAFE_ASSERT(expr) JS_ASSERT(expr)
 # else
 #  define JS_THREADSAFE_ASSERT(expr) ((void) 0)
 # endif
 #else
 # 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,
  * call to the debugger or other moral equivalent as well as causing the
  * entire process to stop.
  */
 extern JS_PUBLIC_API(void) JS_Abort(void);
 
--- a/mfbt/Assertions.h
+++ b/mfbt/Assertions.h
@@ -39,16 +39,97 @@
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef mozilla_Assertions_h_
 #define mozilla_Assertions_h_
 
 #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
  * by MOZ_ASSERT().  (It'll happen eventually, but for just ASSERT()
  * it isn't worth the pain.)  JS_Assert(), although unfortunately
  * named, is part of SpiderMonkey's stable, external API, so this
  * isn't quite as bad as it seems.
  *
  * Once mfbt needs object files, this unholy union with JS_Assert()
--- a/xpcom/ds/nsAtomTable.cpp
+++ b/xpcom/ds/nsAtomTable.cpp
@@ -31,16 +31,18 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
+#include "mozilla/Assertions.h"
+
 #include "nsAtomTable.h"
 #include "nsStaticAtom.h"
 #include "nsString.h"
 #include "nsReadableUtils.h"
 #include "nsUTF8Utils.h"
 #include "nsCRT.h"
 #include "pldhash.h"
 #include "prenv.h"
@@ -401,26 +403,27 @@ GetAtomHashEntry(const PRUnichar* aStrin
   AtomTableKey key(aString, aLength);
   return static_cast<AtomTableEntry*>
                     (PL_DHashTableOperate(&gAtomTable, &key, PL_DHASH_ADD));
 }
 
 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");
   }
 };
 
 nsresult
 NS_RegisterStaticAtoms(const nsStaticAtom* aAtoms, PRUint32 aAtomCount)
 {
   // this does three things:
   // 1) wraps each static atom in a wrapper, if necessary
--- a/xpcom/glue/nsTArray-inl.h
+++ b/xpcom/glue/nsTArray-inl.h
@@ -59,22 +59,22 @@ const nsTArrayHeader* nsTArray_base<Allo
   // Assuming |this| points to an nsAutoArray, we want to get a pointer to
   // mAutoBuf.  So just cast |this| to nsAutoArray* and read &mAutoBuf!
 
   const void* autoBuf = &reinterpret_cast<const nsAutoArrayBase<nsTArray<PRUint32>, 1>*>(this)->mAutoBuf;
 
   // 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<mozilla::AlignedElem<8>, 1>) ==
-                      sizeof(void*) + sizeof(nsTArrayHeader) +
-                      4 + sizeof(mozilla::AlignedElem<8>)));
+  MOZ_STATIC_ASSERT(sizeof(void*) != 4 ||
+                    (MOZ_ALIGNOF(mozilla::AlignedElem<8>) == 8 &&
+                     sizeof(nsAutoTArray<mozilla::AlignedElem<8>, 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.");
   if (sizeof(void*) == 4 && elemAlign == 8) {
     autoBuf = reinterpret_cast<const char*>(autoBuf) + 4;
   }
 
   return reinterpret_cast<const Header*>(autoBuf);
@@ -110,17 +110,18 @@ bool nsTArray_base<Alloc>::UsesAutoArray
   // GetAutoArrayBuffer(8) will always point inside the auto array object,
   // even if it doesn't point at the beginning of the header.
   //
   // Note that this means that we can't store elements with alignment 16 in an
   // nsTArray, because GetAutoArrayBuffer(16) could lie outside the memory
   // 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<const char*>(GetAutoArrayBuffer(8)) -
                    reinterpret_cast<const char*>(GetAutoArrayBuffer(4));
   NS_ABORT_IF_FALSE(diff >= 0 && diff <= 4, "GetAutoArrayBuffer doesn't do what we expect.");
 #endif
 
   return mHdr == GetAutoArrayBuffer(4) || mHdr == GetAutoArrayBuffer(8);
--- a/xpcom/glue/nsTArray.h
+++ b/xpcom/glue/nsTArray.h
@@ -34,25 +34,27 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef nsTArray_h__
 #define nsTArray_h__
 
+#include "mozilla/Assertions.h"
+#include "mozilla/Util.h"
+
 #include <string.h>
 
 #include "prtypes.h"
 #include "nsAlgorithm.h"
 #include "nscore.h"
 #include "nsQuickSort.h"
 #include "nsDebug.h"
 #include "nsTraceRefcnt.h"
-#include "mozilla/Util.h"
 #include NEW_H
 
 //
 // NB: nsTArray assumes that your "T" can be memmove()d.  This is in
 // contrast to STL containers, which follow C++
 // construction/destruction rules.
 //
 // Don't use nsTArray if your "T" can't be memmove()d correctly.
@@ -1311,19 +1313,19 @@ protected:
 
 private:
   // nsTArray_base casts itself as an nsAutoArrayBase in order to get a pointer
   // to mAutoBuf.
   template<class Allocator>
   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<Header*>(&mAutoBuf);
     base_type::Hdr()->mLength = 0;
     base_type::Hdr()->mCapacity = N;
     base_type::Hdr()->mIsAutoArray = 1;
 
     NS_ASSERTION(base_type::GetAutoArrayBuffer(MOZ_ALIGNOF(elem_type)) ==
                  reinterpret_cast<Header*>(&mAutoBuf),
@@ -1362,18 +1364,20 @@ public:
 // because any type may be placed into an array, and there's no padding between
 // elements of an array.)  The compiler pads the end of the structure to
 // enforce this rule.
 //
 // If we used nsAutoTArray<PRUint32, 1> below, this assertion would fail on a
 // 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<PRUint32, 2>) ==
-                 sizeof(void*) + sizeof(nsTArrayHeader) + sizeof(PRUint32) * 2);
+MOZ_STATIC_ASSERT(sizeof(nsAutoTArray<PRUint32, 2>) ==
+                  sizeof(void*) + sizeof(nsTArrayHeader) + sizeof(PRUint32) * 2,
+                  "nsAutoTArray shouldn't contain any extra padding, "
+                  "see the comment");
 
 template<class E, PRUint32 N>
 class AutoFallibleTArray : public nsAutoArrayBase<FallibleTArray<E>, N>
 {
   typedef nsAutoArrayBase<FallibleTArray<E>, N> Base;
 
 public:
   AutoFallibleTArray() {}