diff --git a/js/public/RootingAPI.h b/js/public/RootingAPI.h index bf48234811e..be4b12ce611 100644 --- a/js/public/RootingAPI.h +++ b/js/public/RootingAPI.h @@ -506,11 +506,19 @@ class MOZ_STACK_CLASS MutableHandle : public js::MutableHandleBase public: inline MutableHandle(Rooted *root); inline MutableHandle(PersistentRooted *root); - MutableHandle(int) MOZ_DELETE; -#ifdef MOZ_HAVE_CXX11_NULLPTR - MutableHandle(decltype(nullptr)) MOZ_DELETE; -#endif + private: + // Disallow true nullptr and emulated nullptr (gcc 4.4/4.5, __null, appears + // as int/long [32/64-bit]) for overloading purposes. + template + MutableHandle(N, + typename mozilla::EnableIf::value || + mozilla::IsSame::value || + mozilla::IsSame::value, + int>::Type dummy = 0) + MOZ_DELETE; + + public: void set(T v) { JS_ASSERT(!js::GCMethods::poisoned(v)); *ptr = v; diff --git a/mfbt/Move.h b/mfbt/Move.h index f7d39ffa9b2..8a573f4a8a8 100644 --- a/mfbt/Move.h +++ b/mfbt/Move.h @@ -132,7 +132,7 @@ namespace mozilla { * * To avoid this, C++11 has tweaks to make it possible to write what you mean. * The four constructor overloads above can be written as one constructor - * template like so: + * template like so[0]: * * template * C::C(XArg&& x, YArg&& y) : x(Forward(x)), y(Forward(y)) { } @@ -180,6 +180,28 @@ namespace mozilla { * 'std::forward', which are just like our 'Move' and 'Forward'; but those * definitions aren't available in that header on all our platforms, so we * define them ourselves here.) + * + * 0. This pattern is known as "perfect forwarding". Interestingly, it is not + * actually perfect, and it can't forward all possible argument expressions! + * There are two issues: one that's a C++11 issue, and one that's a legacy + * compiler issue. + * + * The C++11 issue is that you can't form a reference to a bit-field. As a + * workaround, assign the bit-field to a local variable and use that: + * + * // C is as above + * struct S { int x : 1; } s; + * C(s.x, 0); // BAD: s.x is a reference to a bit-field, can't form those + * int tmp = s.x; + * C(tmp, 0); // OK: tmp not a bit-field + * + * The legacy issue is that when we don't have true nullptr and must emulate + * it (gcc 4.4/4.5), forwarding |nullptr| results in an |int| or |long| + * forwarded reference. But such a reference, even if its value is a null + * pointer constant expression, is not itself a null pointer constant + * expression. This causes -Werror=conversion-null errors and pointer-to- + * integer comparison errors. Until we always have true nullptr, users of + * forwarding methods must not pass |nullptr| to them. */ /** diff --git a/mfbt/NullPtr.h b/mfbt/NullPtr.h index 35faadc4c30..6fd70fd116c 100644 --- a/mfbt/NullPtr.h +++ b/mfbt/NullPtr.h @@ -13,12 +13,10 @@ #define mozilla_NullPtr_h #if defined(__clang__) -# ifndef __has_extension -# define __has_extension __has_feature -# endif -# if __has_extension(cxx_nullptr) -# define MOZ_HAVE_CXX11_NULLPTR +# if !__has_extension(cxx_nullptr) +# error "clang version natively supporting nullptr is required." # endif +# define MOZ_HAVE_CXX11_NULLPTR #elif defined(__GNUC__) # if defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L # include "mozilla/Compiler.h" @@ -26,23 +24,89 @@ # define MOZ_HAVE_CXX11_NULLPTR # endif # endif -#elif _MSC_VER >= 1600 -# define MOZ_HAVE_CXX11_NULLPTR +#elif defined(_MSC_VER) + // The minimum supported MSVC (10, _MSC_VER 1600) supports nullptr. +# define MOZ_HAVE_CXX11_NULLPTR #endif -/** - * Use C++11 nullptr if available; otherwise use __null for gcc, or a 0 literal - * with the correct size to match the size of a pointer on a given platform. - */ +namespace mozilla { -#ifndef MOZ_HAVE_CXX11_NULLPTR -# if defined(__GNUC__) -# define nullptr __null -# elif defined(_WIN64) -# define nullptr 0LL -# else -# define nullptr 0L -# endif +/** + * IsNullPointer::value is true iff T is the type of C++11's nullptr. If + * nullptr is emulated, IsNullPointer::value is false for all T. + * + * IsNullPointer is useful to give an argument the true decltype(nullptr) type. + * decltype(nullptr) doesn't work when nullptr is emulated. The simplest + * workaround is to use template overloading and SFINAE to expose an overload + * only if an argument's type is decltype(nullptr). Some examples (that assume + * namespace mozilla has been opened, for simplicity): + * + * // foo(T*), foo(stuff that converts to T*), and foo(decltype(nullptr)) + * // (only invoked if nullptr is true nullptr, otherwise foo(T*) is invoked) + * // but nothing else + * void foo(T*) { } + * template + * void foo(N, + * typename EnableIf::value, int>::Type dummy = 0) + * { } + * + * // foo(T*) *exactly* and foo(decltype(nullptr)), nothing else + * void foo(T*) { } + * template + * void foo(U, + * typename EnableIf::value, int>::Type dummy = 0) + * MOZ_DELETE; + * + * The exact details of how set up the SFINAE bits vary on a case-by-case basis. + * If you need help with this (and unless you've internalized way more sadmaking + * nullptr-emulation knowledge than you should have, you do), feel free to poke + * the person with blame on this comment with questions. :-) + * + * Ideally this would be in TypeTraits.h, but C++11 omitted std::is_null_pointer + * (fixed in C++1y), so in the interests of easing a switch to , + * this trait lives elsewhere. + */ +template +struct IsNullPointer { static const bool value = false; }; + +} // namespace mozilla + +/** + * mozilla::NullptrT is a type that's sort of like decltype(nullptr). But it + * can't be identical, because emulated nullptr doesn't have a distinct type. + * Only with gcc 4.4/4.5, emulated nullptr is __null, and decltype(__null) is + * int or long. But passing __null to an int/long parameter triggers + * -Werror=conversion-null errors with gcc 4.5, or (depending on subsequent use + * inside the overloaded function) can trigger pointer-to-integer comparison + * compiler errors. So fairly often, actually, NullptrT is *not* what you want. + * + * Instead, often you should use template-based overloading in concert with + * SFINAE to add a nullptr overload -- see the comments by IsNullPointer. + * + * So when *should* you use NullptrT? Thus far, the only truly good use seems + * to be as an argument type for operator overloads (because C++ doesn't allow + * operator= to have more than one argument, operator== to have more than two, + * &c.). But even in such cases, it really only works if there are no other + * overloads of the operator that accept a pointer type. If you want both T* + * and nullptr_t overloads, you'll have to wait til we drop gcc 4.4/4.5 support. + * (Currently b2g is the only impediment to this.) + */ +#ifdef MOZ_HAVE_CXX11_NULLPTR + // decltype does the right thing for actual nullptr. + namespace mozilla { + typedef decltype(nullptr) NullptrT; + template<> + struct IsNullPointer { static const bool value = true; }; + } +# undef MOZ_HAVE_CXX11_NULLPTR +#elif MOZ_IS_GCC +# define nullptr __null + // void* sweeps up more than just nullptr, but compilers supporting true + // nullptr are the majority now, so they should detect mistakes. If you're + // feeling paranoid, check/assert that your NullptrT equals nullptr. + namespace mozilla { typedef void* NullptrT; } +#else +# error "No compiler support for nullptr or its emulation." #endif #endif /* mozilla_NullPtr_h */ diff --git a/parser/html/jArray.h b/parser/html/jArray.h index feeea2a4aaa..2f80fdab701 100644 --- a/parser/html/jArray.h +++ b/parser/html/jArray.h @@ -100,24 +100,9 @@ class autoJArray { arr = other.arr; length = other.length; } -#if defined(MOZ_HAVE_CXX11_NULLPTR) -# if defined(__clang__) || defined(_STLPORT_VERSION) - // clang on OS X 10.7 and Android's STLPort do not have std::nullptr_t - typedef decltype(nullptr) jArray_nullptr_t; -# else - // decltype(nullptr) does not evaluate to std::nullptr_t on GCC 4.6.3 - typedef std::nullptr_t jArray_nullptr_t; -# endif -#elif defined(__GNUC__) - typedef void* jArray_nullptr_t; -#elif defined(_WIN64) - typedef uint64_t jArray_nullptr_t; -#else - typedef uint32_t jArray_nullptr_t; -#endif - void operator=(jArray_nullptr_t zero) { + void operator=(mozilla::NullptrT n) { // Make assigning null to an array in Java delete the buffer in C++ - // MSVC10 does not allow asserting that zero is null. + MOZ_ASSERT(n == nullptr); delete[] arr; arr = nullptr; length = 0; diff --git a/xpcom/base/nscore.h b/xpcom/base/nscore.h index c21c017ce7d..690fd8ca977 100644 --- a/xpcom/base/nscore.h +++ b/xpcom/base/nscore.h @@ -25,7 +25,9 @@ #include #include -#include "mozilla/NullPtr.h" +#ifdef __cplusplus +# include "mozilla/NullPtr.h" +#endif /* Core XPCOM declarations. */ diff --git a/xpcom/glue/nsCOMPtr.h b/xpcom/glue/nsCOMPtr.h index d446baffb86..adf457e7507 100644 --- a/xpcom/glue/nsCOMPtr.h +++ b/xpcom/glue/nsCOMPtr.h @@ -146,22 +146,25 @@ struct already_AddRefed |nsCOMPtr_helper|. */ { -#ifdef MOZ_HAVE_CXX11_NULLPTR - /* We use decltype(nullptr) instead of std::nullptr_t because the standard - * library might be old, and to save including . All compilers - * that support nullptr seem to support decltype. */ - already_AddRefed(decltype(nullptr) aNullPtr) + /* + * Prohibit all one-argument overloads but already_AddRefed(T*) and + * already_AddRefed(decltype(nullptr)), and funnel the nullptr case through + * the T* constructor. + */ + template + already_AddRefed(N, + typename mozilla::EnableIf::value, + int>::Type dummy = 0) : mRawPtr(nullptr) { + // nothing else to do here } - explicit -#endif already_AddRefed( T* aRawPtr ) - : mRawPtr(aRawPtr) - { - // nothing else to do here - } + : mRawPtr(aRawPtr) + { + // nothing else to do here + } T* get() const { return mRawPtr; }