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 83240 8d4a9617fcd105c8e06d80abfd4b0c3de47ff486
parent 83239 0d7567cf0a67a44906ab85ba9f2acc6ead70ba63
child 83241 d9e4c34ca1c603d61fe986249324090c84d0b146
push id21742
push userbmo@edmorley.co.uk
push dateThu, 22 Dec 2011 11:29:57 +0000
treeherdermozilla-central@c5b90ea7e475 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs712129
milestone12.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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() {}