☠☠ backed out by c67a79064fd4 ☠ ☠ | |
author | Benoit Jacob <bjacob@mozilla.com> |
Fri, 25 Apr 2014 22:34:04 -0400 | |
changeset 180370 | 080ff1f864652fed62dddc70ed1d5e02ef9e6809 |
parent 180369 | 1defae2eeae9be7434de2e3624af5d2a688e6366 |
child 180371 | 4ad1f662f521eba6514463d13aee536dd17c45ca |
push id | 26663 |
push user | ryanvm@gmail.com |
push date | Sun, 27 Apr 2014 01:52:51 +0000 |
treeherder | mozilla-central@fcf19894d9f3 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | Waldo |
bugs | 987290 |
milestone | 31.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
|
--- a/mfbt/TypedEnum.h +++ b/mfbt/TypedEnum.h @@ -4,50 +4,21 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* Macros to emulate C++11 typed enums and enum classes. */ #ifndef mozilla_TypedEnum_h #define mozilla_TypedEnum_h -#include "mozilla/Attributes.h" +#include "mozilla/TypedEnumInternal.h" #include "mozilla/MacroArgs.h" #if defined(__cplusplus) -#if defined(__clang__) - /* - * Per Clang documentation, "Note that marketing version numbers should not - * be used to check for language features, as different vendors use different - * numbering schemes. Instead, use the feature checking macros." - */ -# ifndef __has_extension -# define __has_extension __has_feature /* compatibility, for older versions of clang */ -# endif -# if __has_extension(cxx_strong_enums) -# define MOZ_HAVE_CXX11_ENUM_TYPE -# define MOZ_HAVE_CXX11_STRONG_ENUMS -# endif -#elif defined(__GNUC__) -# if defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L -# if MOZ_GCC_VERSION_AT_LEAST(4, 6, 3) -# define MOZ_HAVE_CXX11_ENUM_TYPE -# define MOZ_HAVE_CXX11_STRONG_ENUMS -# endif -# endif -#elif defined(_MSC_VER) -# if _MSC_VER >= 1400 -# define MOZ_HAVE_CXX11_ENUM_TYPE -# endif -# if _MSC_VER >= 1700 -# define MOZ_HAVE_CXX11_STRONG_ENUMS -# endif -#endif - /** * MOZ_ENUM_TYPE specifies the underlying numeric type for an enum. It's * specified by placing MOZ_ENUM_TYPE(type) immediately after the enum name in * its declaration, and before the opening curly brace, like * * enum MyEnum MOZ_ENUM_TYPE(uint16_t) * { * A, @@ -185,19 +156,23 @@ class Name \ { \ public: \ enum Enum MOZ_ENUM_TYPE(type) \ { # define MOZ_END_NESTED_ENUM_CLASS(Name) \ }; \ Name() {} \ - Name(Enum aEnum) : mEnum(aEnum) {} \ - explicit Name(int num) : mEnum((Enum)num) {} \ - operator Enum() const { return mEnum; } \ + MOZ_CONSTEXPR Name(Enum aEnum) : mEnum(aEnum) {} \ + template<typename Other> \ + explicit MOZ_CONSTEXPR Name(Other num) : mEnum((Enum)num) {} \ + MOZ_CONSTEXPR operator Enum() const { return mEnum; } \ + explicit MOZ_CONSTEXPR Name(const mozilla::CastableTypedEnumResult<Name>& aOther) \ + : mEnum(aOther.get()) \ + {} \ private: \ Enum mEnum; \ }; # define MOZ_FINISH_NESTED_ENUM_CLASS(Name) \ inline int operator+(const int&, const Name::Enum&) MOZ_DELETE; \ inline int operator+(const Name::Enum&, const int&) MOZ_DELETE; \ inline int operator-(const int&, const Name::Enum&) MOZ_DELETE; \ inline int operator-(const Name::Enum&, const int&) MOZ_DELETE; \ @@ -225,17 +200,16 @@ inline bool operator>=(const Name::Enum&, const int&) MOZ_DELETE; \ inline bool operator<=(const int&, const Name::Enum&) MOZ_DELETE; \ inline bool operator<=(const Name::Enum&, const int&) MOZ_DELETE; \ inline bool operator!(const Name::Enum&) MOZ_DELETE; \ inline bool operator&&(const bool&, const Name::Enum&) MOZ_DELETE; \ inline bool operator&&(const Name::Enum&, const bool&) MOZ_DELETE; \ inline bool operator||(const bool&, const Name::Enum&) MOZ_DELETE; \ inline bool operator||(const Name::Enum&, const bool&) MOZ_DELETE; \ - inline int operator~(const Name::Enum&) MOZ_DELETE; \ inline int operator&(const int&, const Name::Enum&) MOZ_DELETE; \ inline int operator&(const Name::Enum&, const int&) MOZ_DELETE; \ inline int operator|(const int&, const Name::Enum&) MOZ_DELETE; \ inline int operator|(const Name::Enum&, const int&) MOZ_DELETE; \ inline int operator^(const int&, const Name::Enum&) MOZ_DELETE; \ inline int operator^(const Name::Enum&, const int&) MOZ_DELETE; \ inline int operator<<(const int&, const Name::Enum&) MOZ_DELETE; \ inline int operator<<(const Name::Enum&, const int&) MOZ_DELETE; \
new file mode 100644 --- /dev/null +++ b/mfbt/TypedEnumBits.h @@ -0,0 +1,182 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS allows using a typed enum as bit flags. */ + +#ifndef mozilla_TypedEnumBits_h +#define mozilla_TypedEnumBits_h + +#include "mozilla/IntegerTypeTraits.h" +#include "mozilla/TypedEnumInternal.h" + +namespace mozilla { + +#define MOZ_CASTABLETYPEDENUMRESULT_BINOP(Op, OtherType, ReturnType) \ +template<typename E> \ +MOZ_CONSTEXPR ReturnType \ +operator Op(const OtherType& e, const CastableTypedEnumResult<E>& r) \ +{ \ + return ReturnType(e Op OtherType(r)); \ +} \ +template<typename E> \ +MOZ_CONSTEXPR ReturnType \ +operator Op(const CastableTypedEnumResult<E>& r, const OtherType& e) \ +{ \ + return ReturnType(OtherType(r) Op e); \ +} \ +template<typename E> \ +MOZ_CONSTEXPR ReturnType \ +operator Op(const CastableTypedEnumResult<E>& r1, \ + const CastableTypedEnumResult<E>& r2) \ +{ \ + return ReturnType(OtherType(r1) Op OtherType(r2)); \ +} + +MOZ_CASTABLETYPEDENUMRESULT_BINOP(|, E, CastableTypedEnumResult<E>) +MOZ_CASTABLETYPEDENUMRESULT_BINOP(&, E, CastableTypedEnumResult<E>) +MOZ_CASTABLETYPEDENUMRESULT_BINOP(^, E, CastableTypedEnumResult<E>) +MOZ_CASTABLETYPEDENUMRESULT_BINOP(==, E, bool) +MOZ_CASTABLETYPEDENUMRESULT_BINOP(!=, E, bool) +MOZ_CASTABLETYPEDENUMRESULT_BINOP(||, bool, bool) +MOZ_CASTABLETYPEDENUMRESULT_BINOP(&&, bool, bool) + +template <typename E> +MOZ_CONSTEXPR CastableTypedEnumResult<E> +operator ~(const CastableTypedEnumResult<E>& r) +{ + return CastableTypedEnumResult<E>(~(E(r))); +} + +#define MOZ_CASTABLETYPEDENUMRESULT_COMPOUND_ASSIGN_OP(Op) \ +template<typename E> \ +E& \ +operator Op(E& r1, \ + const CastableTypedEnumResult<E>& r2) \ +{ \ + return r1 Op E(r2); \ +} + +MOZ_CASTABLETYPEDENUMRESULT_COMPOUND_ASSIGN_OP(&=) +MOZ_CASTABLETYPEDENUMRESULT_COMPOUND_ASSIGN_OP(|=) +MOZ_CASTABLETYPEDENUMRESULT_COMPOUND_ASSIGN_OP(^=) + +#undef MOZ_CASTABLETYPEDENUMRESULT_COMPOUND_ASSIGN_OP + +#undef MOZ_CASTABLETYPEDENUMRESULT_BINOP + +#ifndef MOZ_HAVE_CXX11_STRONG_ENUMS + +#define MOZ_CASTABLETYPEDENUMRESULT_BINOP_EXTRA_NON_CXX11(Op, ReturnType) \ +template<typename E> \ +MOZ_CONSTEXPR ReturnType \ +operator Op(typename E::Enum e, const CastableTypedEnumResult<E>& r) \ +{ \ + return ReturnType(e Op E(r)); \ +} \ +template<typename E> \ +MOZ_CONSTEXPR ReturnType \ +operator Op(const CastableTypedEnumResult<E>& r, typename E::Enum e) \ +{ \ + return ReturnType(E(r) Op e); \ +} + +MOZ_CASTABLETYPEDENUMRESULT_BINOP_EXTRA_NON_CXX11(|, CastableTypedEnumResult<E>) +MOZ_CASTABLETYPEDENUMRESULT_BINOP_EXTRA_NON_CXX11(&, CastableTypedEnumResult<E>) +MOZ_CASTABLETYPEDENUMRESULT_BINOP_EXTRA_NON_CXX11(^, CastableTypedEnumResult<E>) +MOZ_CASTABLETYPEDENUMRESULT_BINOP_EXTRA_NON_CXX11(==, bool) +MOZ_CASTABLETYPEDENUMRESULT_BINOP_EXTRA_NON_CXX11(!=, bool) + +#undef MOZ_CASTABLETYPEDENUMRESULT_BINOP_EXTRA_NON_CXX11 + +#endif // not MOZ_HAVE_CXX11_STRONG_ENUMS + +namespace detail { +template<typename E> +struct UnsignedIntegerTypeForEnum + : UnsignedStdintTypeForSize<sizeof(E)> +{}; +} + +} // namespace mozilla + +#define MOZ_MAKE_ENUM_CLASS_BINOP_IMPL(Name, Op) \ + inline MOZ_CONSTEXPR mozilla::CastableTypedEnumResult<Name> \ + operator Op(Name a, Name b) \ + { \ + typedef mozilla::CastableTypedEnumResult<Name> Result; \ + typedef mozilla::detail::UnsignedIntegerTypeForEnum<Name>::Type U; \ + return Result(Name(U(a) Op U(b))); \ + } \ + \ + inline Name& \ + operator Op##=(Name& a, Name b) \ + { \ + return a = a Op b; \ + } + +#define MOZ_MAKE_ENUM_CLASS_OPS_IMPL(Name) \ + MOZ_MAKE_ENUM_CLASS_BINOP_IMPL(Name, |) \ + MOZ_MAKE_ENUM_CLASS_BINOP_IMPL(Name, &) \ + MOZ_MAKE_ENUM_CLASS_BINOP_IMPL(Name, ^) \ + inline MOZ_CONSTEXPR mozilla::CastableTypedEnumResult<Name> \ + operator~(Name a) \ + { \ + typedef mozilla::CastableTypedEnumResult<Name> Result; \ + typedef mozilla::detail::UnsignedIntegerTypeForEnum<Name>::Type U; \ + return Result(Name(~(U(a)))); \ + } + +#ifndef MOZ_HAVE_CXX11_STRONG_ENUMS +# define MOZ_MAKE_ENUM_CLASS_BITWISE_BINOP_EXTRA_NON_CXX11(Name, Op) \ + inline MOZ_CONSTEXPR mozilla::CastableTypedEnumResult<Name> \ + operator Op(Name a, Name::Enum b) \ + { \ + return a Op Name(b); \ + } \ + \ + inline MOZ_CONSTEXPR mozilla::CastableTypedEnumResult<Name> \ + operator Op(Name::Enum a, Name b) \ + { \ + return Name(a) Op b; \ + } \ + \ + inline MOZ_CONSTEXPR mozilla::CastableTypedEnumResult<Name> \ + operator Op(Name::Enum a, Name::Enum b) \ + { \ + return Name(a) Op Name(b); \ + } \ + \ + inline Name& \ + operator Op##=(Name& a, Name::Enum b) \ + { \ + return a = a Op Name(b); \ + } + +# define MOZ_MAKE_ENUM_CLASS_OPS_EXTRA_NON_CXX11(Name) \ + MOZ_MAKE_ENUM_CLASS_BITWISE_BINOP_EXTRA_NON_CXX11(Name, |) \ + MOZ_MAKE_ENUM_CLASS_BITWISE_BINOP_EXTRA_NON_CXX11(Name, &) \ + MOZ_MAKE_ENUM_CLASS_BITWISE_BINOP_EXTRA_NON_CXX11(Name, ^) \ + inline MOZ_CONSTEXPR mozilla::CastableTypedEnumResult<Name> \ + operator~(Name::Enum a) \ + { \ + return ~(Name(a)); \ + } +#endif + +/** + * MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS generates standard bitwise operators + * for the given enum type. Use this to enable using an enum type as bit-field. + */ +#ifdef MOZ_HAVE_CXX11_STRONG_ENUMS +# define MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Name) \ + MOZ_MAKE_ENUM_CLASS_OPS_IMPL(Name) +#else +# define MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Name) \ + MOZ_MAKE_ENUM_CLASS_OPS_IMPL(Name) \ + MOZ_MAKE_ENUM_CLASS_OPS_EXTRA_NON_CXX11(Name) +#endif + +#endif // mozilla_TypedEnumBits_h
new file mode 100644 --- /dev/null +++ b/mfbt/TypedEnumInternal.h @@ -0,0 +1,112 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Internal stuff needed by TypedEnum.h and TypedEnumBits.h. */ + +// NOTE: When we can assume C++11 enum class support and TypedEnum.h goes away, +// we should then consider folding TypedEnumInternal.h into TypedEnumBits.h. + +#ifndef mozilla_TypedEnumInternal_h +#define mozilla_TypedEnumInternal_h + +#include "mozilla/Attributes.h" + +#if defined(__cplusplus) + +#if defined(__clang__) + /* + * Per Clang documentation, "Note that marketing version numbers should not + * be used to check for language features, as different vendors use different + * numbering schemes. Instead, use the feature checking macros." + */ +# ifndef __has_extension +# define __has_extension __has_feature /* compatibility, for older versions of clang */ +# endif +# if __has_extension(cxx_strong_enums) +# define MOZ_HAVE_CXX11_ENUM_TYPE +# define MOZ_HAVE_CXX11_STRONG_ENUMS +# endif +#elif defined(__GNUC__) +# if defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L +# if MOZ_GCC_VERSION_AT_LEAST(4, 6, 3) +# define MOZ_HAVE_CXX11_ENUM_TYPE +# define MOZ_HAVE_CXX11_STRONG_ENUMS +# endif +# endif +#elif defined(_MSC_VER) +# if _MSC_VER >= 1400 +# define MOZ_HAVE_CXX11_ENUM_TYPE +# endif +# if _MSC_VER >= 1700 +# define MOZ_HAVE_CXX11_STRONG_ENUMS +# endif +#endif + +namespace mozilla { + +/* + * The problem that CastableTypedEnumResult aims to solve is that + * typed enums are not convertible to bool, and there is no way to make them + * be, yet user code wants to be able to write + * + * if (myFlags & Flags::SOME_PARTICULAR_FLAG) (1) + * + * There are different approaches to solving this. Most of them require + * adapting user code. For example, we could implement operator! and have + * the user write + * + * if (!!(myFlags & Flags::SOME_PARTICULAR_FLAG)) (2) + * + * Or we could supply a IsNonZero() or Any() function returning whether + * an enum value is nonzero, and have the user write + * + * if (Any(Flags & Flags::SOME_PARTICULAR_FLAG)) (3) + * + * But instead, we choose to preserve the original user syntax (1) as it + * is inherently more readable, and to ease porting existing code to typed + * enums. We achieve this by having operator& and other binary bitwise + * operators have as return type a class, CastableTypedEnumResult, + * that wraps a typed enum but adds bool convertibility. + */ +template<typename E> +class CastableTypedEnumResult +{ + private: + const E mValue; + + public: + explicit MOZ_CONSTEXPR CastableTypedEnumResult(E value) + : mValue(value) + {} + + MOZ_CONSTEXPR operator E() const { return mValue; } + + template<typename DestinationType> + MOZ_EXPLICIT_CONVERSION MOZ_CONSTEXPR + operator DestinationType() const { + return DestinationType(mValue); + } + + MOZ_CONSTEXPR bool operator !() const { return !bool(mValue); } + +#ifndef MOZ_HAVE_CXX11_STRONG_ENUMS + // This get() method is used to implement a constructor in the + // non-c++11 fallback path for MOZ_BEGIN_ENUM_CLASS, taking a + // CastableTypedEnumResult. If we try to implement it using the + // above conversion operator E(), then at least clang 3.3 + // (when forced to take the non-c++11 fallback path) compiles + // this constructor to an infinite recursion. So we introduce this + // get() method, that does exactly the same as the conversion operator, + // to work around this. + MOZ_CONSTEXPR E get() const { return mValue; } +#endif +}; + +} // namespace mozilla + +#endif // __cplusplus + +#endif // mozilla_TypedEnumInternal_h
--- a/mfbt/moz.build +++ b/mfbt/moz.build @@ -56,16 +56,18 @@ EXPORTS.mozilla = [ 'RefPtr.h', 'RollingMean.h', 'Scoped.h', 'SHA1.h', 'SplayTree.h', 'TemplateLib.h', 'ThreadLocal.h', 'TypedEnum.h', + 'TypedEnumBits.h', + 'TypedEnumInternal.h', 'Types.h', 'TypeTraits.h', 'Vector.h', 'WeakPtr.h', ] if CONFIG['OS_ARCH'] == 'WINNT': EXPORTS.mozilla += [
--- a/mfbt/tests/TestTypedEnum.cpp +++ b/mfbt/tests/TestTypedEnum.cpp @@ -1,40 +1,568 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/Assertions.h" #include "mozilla/TypedEnum.h" +#include "mozilla/TypedEnumBits.h" + +#include <stdint.h> + +// A rough feature check for is_literal_type. Not very carefully checked. +// Feel free to amend as needed. +// We leave ANDROID out because it's using stlport which doesn't have std::is_literal_type. +#if __cplusplus >= 201103L && !defined(ANDROID) +# if defined(__clang__) + /* + * Per Clang documentation, "Note that marketing version numbers should not + * be used to check for language features, as different vendors use different + * numbering schemes. Instead, use the feature checking macros." + */ +# ifndef __has_extension +# define __has_extension __has_feature /* compatibility, for older versions of clang */ +# endif +# if __has_extension(is_literal) && __has_include(<type_traits>) +# define MOZ_HAVE_IS_LITERAL +# endif +# elif defined(__GNUC__) +# if defined(__GXX_EXPERIMENTAL_CXX0X__) +# if MOZ_GCC_VERSION_AT_LEAST(4, 6, 0) +# define MOZ_HAVE_IS_LITERAL +# endif +# endif +# elif defined(_MSC_VER) +# if _MSC_VER >= 1700 +# define MOZ_HAVE_IS_LITERAL +# endif +# endif +#endif + +#ifdef MOZ_HAVE_IS_LITERAL +#include <type_traits> +template<typename T> +void +RequireLiteralType() +{ + static_assert(std::is_literal_type<T>::value, "Expected a literal type"); +} +#else // not MOZ_HAVE_IS_LITERAL +template<typename T> +void +RequireLiteralType() +{ +} +#endif + +template<typename T> +void +RequireLiteralType(const T&) +{ + RequireLiteralType<T>(); +} MOZ_BEGIN_ENUM_CLASS(AutoEnum) A, - B + B = -3, + C MOZ_END_ENUM_CLASS(AutoEnum) - MOZ_BEGIN_ENUM_CLASS(CharEnum, char) A, - B + B = 3, + C MOZ_END_ENUM_CLASS(CharEnum) +MOZ_BEGIN_ENUM_CLASS(AutoEnumBitField) + A = 0x10, + B = 0x20, + C +MOZ_END_ENUM_CLASS(AutoEnumBitField) + +MOZ_BEGIN_ENUM_CLASS(CharEnumBitField, char) + A = 0x10, + B, + C = 0x40 +MOZ_END_ENUM_CLASS(CharEnumBitField) + struct Nested { MOZ_BEGIN_NESTED_ENUM_CLASS(AutoEnum) - C + A, + B, + C = -1 MOZ_END_NESTED_ENUM_CLASS(AutoEnum) MOZ_BEGIN_NESTED_ENUM_CLASS(CharEnum, char) - D = 4, - E + A = 4, + B, + C = 1 MOZ_END_NESTED_ENUM_CLASS(CharEnum) + + MOZ_BEGIN_NESTED_ENUM_CLASS(AutoEnumBitField) + A, + B = 0x20, + C + MOZ_END_NESTED_ENUM_CLASS(AutoEnumBitField) + + MOZ_BEGIN_NESTED_ENUM_CLASS(CharEnumBitField, char) + A = 1, + B = 1, + C = 1 + MOZ_END_NESTED_ENUM_CLASS(CharEnumBitField) }; -// Simply check that this compiles. -const AutoEnum autoEnum = AutoEnum::A; -const CharEnum charEnum = CharEnum::B; -const Nested::AutoEnum nestedAutoEnum = Nested::AutoEnum::C; -const Nested::CharEnum nestedCharEnum = Nested::CharEnum::D; +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(AutoEnumBitField) +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(CharEnumBitField) +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Nested::AutoEnumBitField) +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Nested::CharEnumBitField) + +#define MAKE_STANDARD_BITFIELD_FOR_TYPE(IntType) \ + MOZ_BEGIN_ENUM_CLASS(BitFieldFor_##IntType, IntType) \ + A = 1, \ + B = 2, \ + C = 4, \ + MOZ_END_ENUM_CLASS(BitFieldFor_##IntType) \ + MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(BitFieldFor_##IntType) + +MAKE_STANDARD_BITFIELD_FOR_TYPE(int8_t) +MAKE_STANDARD_BITFIELD_FOR_TYPE(uint8_t) +MAKE_STANDARD_BITFIELD_FOR_TYPE(int16_t) +MAKE_STANDARD_BITFIELD_FOR_TYPE(uint16_t) +MAKE_STANDARD_BITFIELD_FOR_TYPE(int32_t) +MAKE_STANDARD_BITFIELD_FOR_TYPE(uint32_t) +MAKE_STANDARD_BITFIELD_FOR_TYPE(int64_t) +MAKE_STANDARD_BITFIELD_FOR_TYPE(uint64_t) +MAKE_STANDARD_BITFIELD_FOR_TYPE(char) +typedef signed char signed_char; +MAKE_STANDARD_BITFIELD_FOR_TYPE(signed_char) +typedef unsigned char unsigned_char; +MAKE_STANDARD_BITFIELD_FOR_TYPE(unsigned_char) +MAKE_STANDARD_BITFIELD_FOR_TYPE(short) +typedef unsigned short unsigned_short; +MAKE_STANDARD_BITFIELD_FOR_TYPE(unsigned_short) +MAKE_STANDARD_BITFIELD_FOR_TYPE(int) +typedef unsigned int unsigned_int; +MAKE_STANDARD_BITFIELD_FOR_TYPE(unsigned_int) +MAKE_STANDARD_BITFIELD_FOR_TYPE(long) +typedef unsigned long unsigned_long; +MAKE_STANDARD_BITFIELD_FOR_TYPE(unsigned_long) +typedef long long long_long; +MAKE_STANDARD_BITFIELD_FOR_TYPE(long_long) +typedef unsigned long long unsigned_long_long; +MAKE_STANDARD_BITFIELD_FOR_TYPE(unsigned_long_long) + +#undef MAKE_STANDARD_BITFIELD_FOR_TYPE + +template<typename T> +void +TestNonConvertibilityForOneType() +{ + using mozilla::IsConvertible; +#ifdef MOZ_HAVE_CXX11_STRONG_ENUMS + static_assert(!IsConvertible<T, bool>::value, "should not be convertible"); + static_assert(!IsConvertible<T, int>::value, "should not be convertible"); + static_assert(!IsConvertible<T, uint64_t>::value, "should not be convertible"); +#endif + + static_assert(!IsConvertible<bool, T>::value, "should not be convertible"); + static_assert(!IsConvertible<int, T>::value, "should not be convertible"); + static_assert(!IsConvertible<uint64_t, T>::value, "should not be convertible"); +} + +template<typename TypedEnum> +void +TestTypedEnumBasics() +{ + const TypedEnum a = TypedEnum::A; + int unused = int(a); + (void) unused; + RequireLiteralType(TypedEnum::A); + RequireLiteralType(a); + TestNonConvertibilityForOneType<TypedEnum>(); +} + +// Op wraps a bitwise binary operator, passed as a char template parameter, +// and applies it to its arguments (t1, t2). For example, +// +// Op<'|'>(t1, t2) +// +// is the same as +// +// t1 | t2. +// +template<char o, typename T1, typename T2> +auto Op(const T1& t1, const T2& t2) + -> decltype(t1 | t2) // See the static_assert's below --- the return type + // depends solely on the operands type, not on the + // choice of operation. +{ + using mozilla::IsSame; + static_assert(IsSame<decltype(t1 | t2), decltype(t1 & t2)>::value, + "binary ops should have the same result type"); + static_assert(IsSame<decltype(t1 | t2), decltype(t1 ^ t2)>::value, + "binary ops should have the same result type"); + + static_assert(o == '|' || + o == '&' || + o == '^', "unexpected operator character"); + + return o == '|' ? t1 | t2 + : o == '&' ? t1 & t2 + : t1 ^ t2; +} + +// OpAssign wraps a bitwise binary operator, passed as a char template +// parameter, and applies the corresponding compound-assignment operator to its +// arguments (t1, t2). For example, +// +// OpAssign<'|'>(t1, t2) +// +// is the same as +// +// t1 |= t2. +// +template<char o, typename T1, typename T2> +T1& OpAssign(T1& t1, const T2& t2) +{ + static_assert(o == '|' || + o == '&' || + o == '^', "unexpected operator character"); + + switch (o) { + case '|': return t1 |= t2; + case '&': return t1 &= t2; + case '^': return t1 ^= t2; + default: MOZ_CRASH(); + } +} + +// Tests a single binary bitwise operator, using a single set of three operands. +// The operations tested are: +// +// result = t1 Op t2; +// result Op= t3; +// +// Where Op is the operator specified by the char template parameter 'o' and can +// be any of '|', '&', '^'. +// +// Note that the operands t1, t2, t3 are intentionally passed with free types +// (separate template parameters for each) because their type may actually be +// different from TypedEnum: +// 1) Their type could be CastableTypedEnumResult<TypedEnum> if they are +// the result of a bitwise operation themselves; +// 2) In the non-c++11 legacy path, the type of enum values is also +// different from TypedEnum. +// +template<typename TypedEnum, char o, typename T1, typename T2, typename T3> +void TestBinOp(const T1& t1, const T2& t2, const T3& t3) +{ + typedef typename mozilla::detail::UnsignedIntegerTypeForEnum<TypedEnum>::Type + UnsignedIntegerType; + + // Part 1: + // Test the bitwise binary operator i.e. + // result = t1 Op t2; + auto result = Op<o>(t1, t2); + + typedef decltype(result) ResultType; + + RequireLiteralType<ResultType>(); + TestNonConvertibilityForOneType<ResultType>(); + + UnsignedIntegerType unsignedIntegerResult + = Op<o>(UnsignedIntegerType(t1), UnsignedIntegerType(t2)); + + MOZ_RELEASE_ASSERT(unsignedIntegerResult == UnsignedIntegerType(result)); + MOZ_RELEASE_ASSERT(unsignedIntegerResult == UnsignedIntegerType(TypedEnum(result))); + MOZ_RELEASE_ASSERT((!unsignedIntegerResult) == (!result)); + MOZ_RELEASE_ASSERT((!!unsignedIntegerResult) == (!!result)); + MOZ_RELEASE_ASSERT(bool(unsignedIntegerResult) == bool(result)); + + // Part 2: + // Test the compound-assignment operator, i.e. + // result Op= t3; + TypedEnum newResult = result; + OpAssign<o>(newResult, t3); + UnsignedIntegerType unsignedIntegerNewResult = unsignedIntegerResult; + OpAssign<o>(unsignedIntegerNewResult, UnsignedIntegerType(t3)); + MOZ_RELEASE_ASSERT(unsignedIntegerNewResult == UnsignedIntegerType(newResult)); + + // Part 3: + // Test additional boolean operators that we unfortunately had to add to + // CastableTypedEnumResult at some point to please some compiler, + // even though bool convertibility should have been enough. + MOZ_RELEASE_ASSERT(result == TypedEnum(result)); + MOZ_RELEASE_ASSERT(!(result != TypedEnum(result))); + MOZ_RELEASE_ASSERT((result && true) == bool(result)); + MOZ_RELEASE_ASSERT((result && false) == false); + MOZ_RELEASE_ASSERT((true && result) == bool(result)); + MOZ_RELEASE_ASSERT((false && result && false) == false); + MOZ_RELEASE_ASSERT((result || false) == bool(result)); + MOZ_RELEASE_ASSERT((result || true) == true); + MOZ_RELEASE_ASSERT((false || result) == bool(result)); + MOZ_RELEASE_ASSERT((true || result) == true); +} + +// Similar to TestBinOp but testing the unary ~ operator. +template<typename TypedEnum, typename T> +void TestTilde(const T& t) +{ + typedef typename mozilla::detail::UnsignedIntegerTypeForEnum<TypedEnum>::Type + UnsignedIntegerType; + + auto result = ~t; + + typedef decltype(result) ResultType; + + RequireLiteralType<ResultType>(); + TestNonConvertibilityForOneType<ResultType>(); + + UnsignedIntegerType unsignedIntegerResult = ~(UnsignedIntegerType(t)); + + MOZ_RELEASE_ASSERT(unsignedIntegerResult == UnsignedIntegerType(result)); + MOZ_RELEASE_ASSERT(unsignedIntegerResult == UnsignedIntegerType(TypedEnum(result))); + MOZ_RELEASE_ASSERT((!unsignedIntegerResult) == (!result)); + MOZ_RELEASE_ASSERT((!!unsignedIntegerResult) == (!!result)); + MOZ_RELEASE_ASSERT(bool(unsignedIntegerResult) == bool(result)); +} + +// Helper dispatching a given triple of operands to all operator-specific +// testing functions. +template<typename TypedEnum, typename T1, typename T2, typename T3> +void TestAllOpsForGivenOperands(const T1& t1, const T2& t2, const T3& t3) +{ + TestBinOp<TypedEnum, '|'>(t1, t2, t3); + TestBinOp<TypedEnum, '&'>(t1, t2, t3); + TestBinOp<TypedEnum, '^'>(t1, t2, t3); + TestTilde<TypedEnum>(t1); +} + +// Helper building various triples of operands using a given operator, +// and testing all operators with them. +template<typename TypedEnum, char o> +void TestAllOpsForOperandsBuiltUsingGivenOp() +{ + // The type of enum values like TypedEnum::A may be different from + // TypedEnum. That is the case in the legacy non-C++11 path. We want to + // ensure good test coverage even when these two types are distinct. + // To that effect, we have both 'auto' typed variables, preserving the + // original type of enum values, and 'plain' typed variables, that + // are plain TypedEnum's. + + const TypedEnum a_plain = TypedEnum::A; + const TypedEnum b_plain = TypedEnum::B; + const TypedEnum c_plain = TypedEnum::C; + + auto a_auto = TypedEnum::A; + auto b_auto = TypedEnum::B; + auto c_auto = TypedEnum::C; + + auto ab_plain = Op<o>(a_plain, b_plain); + auto bc_plain = Op<o>(b_plain, c_plain); + auto ab_auto = Op<o>(a_auto, b_auto); + auto bc_auto = Op<o>(b_auto, c_auto); + + // On each row below, we pass a triple of operands. Keep in mind that this + // is going to be received as (t1, t2, t3) and the actual tests performed + // will be of the form + // + // result = t1 Op t2; + // result Op= t3; + // + // For this reason, we carefully ensure that the values of (t1, t2) + // systematically cover all types of such pairs; to limit complexity, + // we are not so careful with t3, and we just try to pass t3's + // that may lead to nontrivial bitwise operations. + TestAllOpsForGivenOperands<TypedEnum>(a_plain, b_plain, c_plain); + TestAllOpsForGivenOperands<TypedEnum>(a_plain, bc_plain, b_auto); + TestAllOpsForGivenOperands<TypedEnum>(ab_plain, c_plain, a_plain); + TestAllOpsForGivenOperands<TypedEnum>(ab_plain, bc_plain, a_auto); + + TestAllOpsForGivenOperands<TypedEnum>(a_plain, b_auto, c_plain); + TestAllOpsForGivenOperands<TypedEnum>(a_plain, bc_auto, b_auto); + TestAllOpsForGivenOperands<TypedEnum>(ab_plain, c_auto, a_plain); + TestAllOpsForGivenOperands<TypedEnum>(ab_plain, bc_auto, a_auto); + + TestAllOpsForGivenOperands<TypedEnum>(a_auto, b_plain, c_plain); + TestAllOpsForGivenOperands<TypedEnum>(a_auto, bc_plain, b_auto); + TestAllOpsForGivenOperands<TypedEnum>(ab_auto, c_plain, a_plain); + TestAllOpsForGivenOperands<TypedEnum>(ab_auto, bc_plain, a_auto); + + TestAllOpsForGivenOperands<TypedEnum>(a_auto, b_auto, c_plain); + TestAllOpsForGivenOperands<TypedEnum>(a_auto, bc_auto, b_auto); + TestAllOpsForGivenOperands<TypedEnum>(ab_auto, c_auto, a_plain); + TestAllOpsForGivenOperands<TypedEnum>(ab_auto, bc_auto, a_auto); +} + +// Tests all bitwise operations on a given TypedEnum bitfield. +template<typename TypedEnum> +void +TestTypedEnumBitField() +{ + TestTypedEnumBasics<TypedEnum>(); + + TestAllOpsForOperandsBuiltUsingGivenOp<TypedEnum, '|'>(); + TestAllOpsForOperandsBuiltUsingGivenOp<TypedEnum, '&'>(); + TestAllOpsForOperandsBuiltUsingGivenOp<TypedEnum, '^'>(); +} + +// Checks that enum bitwise expressions have the same non-convertibility properties as +// c++11 enum classes do, i.e. not implicitly convertible to anything +// (though *explicitly* convertible). +void TestNoConversionsBetweenUnrelatedTypes() +{ + using mozilla::IsConvertible; + + // Two typed enum classes having the same underlying integer type, to ensure that + // we would catch bugs accidentally allowing conversions in that case. + typedef CharEnumBitField T1; + typedef Nested::CharEnumBitField T2; + + static_assert(!IsConvertible<T1, T2>::value, + "should not be convertible"); + static_assert(!IsConvertible<T1, decltype(T2::A)>::value, + "should not be convertible"); + static_assert(!IsConvertible<T1, decltype(T2::A | T2::B)>::value, + "should not be convertible"); + + static_assert(!IsConvertible<decltype(T1::A), T2>::value, + "should not be convertible"); + static_assert(!IsConvertible<decltype(T1::A), decltype(T2::A)>::value, + "should not be convertible"); + static_assert(!IsConvertible<decltype(T1::A), decltype(T2::A | T2::B)>::value, + "should not be convertible"); + + // The following are #ifdef MOZ_HAVE_EXPLICIT_CONVERSION because + // without support for explicit conversion operators, we can't easily have these + // bad conversions completely removed. They still do fail to compile in practice, + // but not in a way that we can static_assert on. +#ifdef MOZ_HAVE_EXPLICIT_CONVERSION + static_assert(!IsConvertible<decltype(T1::A | T1::B), T2>::value, + "should not be convertible"); + static_assert(!IsConvertible<decltype(T1::A | T1::B), decltype(T2::A)>::value, + "should not be convertible"); + static_assert(!IsConvertible<decltype(T1::A | T1::B), decltype(T2::A | T2::B)>::value, + "should not be convertible"); +#endif +} + +MOZ_BEGIN_ENUM_CLASS(Int8EnumWithHighBits, int8_t) + A = 0x20, + B = 0x40 +MOZ_END_ENUM_CLASS(Int8EnumWithHighBits) +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Int8EnumWithHighBits) + +MOZ_BEGIN_ENUM_CLASS(Uint8EnumWithHighBits, uint8_t) + A = 0x40, + B = 0x80 +MOZ_END_ENUM_CLASS(Uint8EnumWithHighBits) +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Uint8EnumWithHighBits) + +MOZ_BEGIN_ENUM_CLASS(Int16EnumWithHighBits, int16_t) + A = 0x2000, + B = 0x4000 +MOZ_END_ENUM_CLASS(Int16EnumWithHighBits) +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Int16EnumWithHighBits) + +MOZ_BEGIN_ENUM_CLASS(Uint16EnumWithHighBits, uint16_t) + A = 0x4000, + B = 0x8000 +MOZ_END_ENUM_CLASS(Uint16EnumWithHighBits) +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Uint16EnumWithHighBits) + +MOZ_BEGIN_ENUM_CLASS(Int32EnumWithHighBits, int32_t) + A = 0x20000000, + B = 0x40000000 +MOZ_END_ENUM_CLASS(Int32EnumWithHighBits) +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Int32EnumWithHighBits) + +MOZ_BEGIN_ENUM_CLASS(Uint32EnumWithHighBits, uint32_t) + A = 0x40000000u, + B = 0x80000000u +MOZ_END_ENUM_CLASS(Uint32EnumWithHighBits) +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Uint32EnumWithHighBits) + +MOZ_BEGIN_ENUM_CLASS(Int64EnumWithHighBits, int64_t) + A = 0x2000000000000000ll, + B = 0x4000000000000000ll +MOZ_END_ENUM_CLASS(Int64EnumWithHighBits) +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Int64EnumWithHighBits) + +MOZ_BEGIN_ENUM_CLASS(Uint64EnumWithHighBits, uint64_t) + A = 0x4000000000000000ull, + B = 0x8000000000000000ull +MOZ_END_ENUM_CLASS(Uint64EnumWithHighBits) +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Uint64EnumWithHighBits) + +// Checks that we don't accidentally truncate high bits by coercing to the wrong +// integer type internally when implementing bitwise ops. +template<typename EnumType, typename IntType> +void TestIsNotTruncated() +{ + EnumType a = EnumType::A; + EnumType b = EnumType::B; + MOZ_RELEASE_ASSERT(IntType(a)); + MOZ_RELEASE_ASSERT(IntType(b)); + MOZ_RELEASE_ASSERT(a | EnumType::B); + MOZ_RELEASE_ASSERT(a | b); + MOZ_RELEASE_ASSERT(EnumType::A | EnumType::B); + EnumType c = EnumType::A | EnumType::B; + MOZ_RELEASE_ASSERT(IntType(c)); + MOZ_RELEASE_ASSERT(c & c); + MOZ_RELEASE_ASSERT(c | c); + MOZ_RELEASE_ASSERT(c == (EnumType::A | EnumType::B)); + MOZ_RELEASE_ASSERT(a != (EnumType::A | EnumType::B)); + MOZ_RELEASE_ASSERT(b != (EnumType::A | EnumType::B)); + MOZ_RELEASE_ASSERT(c & EnumType::A); + MOZ_RELEASE_ASSERT(c & EnumType::B); + EnumType d = EnumType::A; + d |= EnumType::B; + MOZ_RELEASE_ASSERT(d == c); +} int main() { + TestTypedEnumBasics<AutoEnum>(); + TestTypedEnumBasics<CharEnum>(); + TestTypedEnumBasics<Nested::AutoEnum>(); + TestTypedEnumBasics<Nested::CharEnum>(); + + TestTypedEnumBitField<AutoEnumBitField>(); + TestTypedEnumBitField<CharEnumBitField>(); + TestTypedEnumBitField<Nested::AutoEnumBitField>(); + TestTypedEnumBitField<Nested::CharEnumBitField>(); + + TestTypedEnumBitField<BitFieldFor_uint8_t>(); + TestTypedEnumBitField<BitFieldFor_int8_t>(); + TestTypedEnumBitField<BitFieldFor_uint16_t>(); + TestTypedEnumBitField<BitFieldFor_int16_t>(); + TestTypedEnumBitField<BitFieldFor_uint32_t>(); + TestTypedEnumBitField<BitFieldFor_int32_t>(); + TestTypedEnumBitField<BitFieldFor_uint64_t>(); + TestTypedEnumBitField<BitFieldFor_int64_t>(); + TestTypedEnumBitField<BitFieldFor_char>(); + TestTypedEnumBitField<BitFieldFor_signed_char>(); + TestTypedEnumBitField<BitFieldFor_unsigned_char>(); + TestTypedEnumBitField<BitFieldFor_short>(); + TestTypedEnumBitField<BitFieldFor_unsigned_short>(); + TestTypedEnumBitField<BitFieldFor_int>(); + TestTypedEnumBitField<BitFieldFor_unsigned_int>(); + TestTypedEnumBitField<BitFieldFor_long>(); + TestTypedEnumBitField<BitFieldFor_unsigned_long>(); + TestTypedEnumBitField<BitFieldFor_long_long>(); + TestTypedEnumBitField<BitFieldFor_unsigned_long_long>(); + + TestNoConversionsBetweenUnrelatedTypes(); + + TestIsNotTruncated<Int8EnumWithHighBits, int8_t>(); + TestIsNotTruncated<Int16EnumWithHighBits, int16_t>(); + TestIsNotTruncated<Int32EnumWithHighBits, int32_t>(); + TestIsNotTruncated<Int64EnumWithHighBits, int64_t>(); + TestIsNotTruncated<Uint8EnumWithHighBits, uint8_t>(); + TestIsNotTruncated<Uint16EnumWithHighBits, uint16_t>(); + TestIsNotTruncated<Uint32EnumWithHighBits, uint32_t>(); + TestIsNotTruncated<Uint64EnumWithHighBits, uint64_t>(); + + return 0; }