mfbt/tests/TestTypedEnum.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/mfbt/tests/TestTypedEnum.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,570 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +#include "mozilla/Assertions.h"
     1.9 +#include "mozilla/Attributes.h"
    1.10 +#include "mozilla/TypedEnum.h"
    1.11 +#include "mozilla/TypedEnumBits.h"
    1.12 +
    1.13 +#include <stdint.h>
    1.14 +
    1.15 +// A rough feature check for is_literal_type. Not very carefully checked.
    1.16 +// Feel free to amend as needed.
    1.17 +// We leave ANDROID out because it's using stlport which doesn't have std::is_literal_type.
    1.18 +#if __cplusplus >= 201103L && !defined(ANDROID)
    1.19 +#  if defined(__clang__)
    1.20 +     /*
    1.21 +      * Per Clang documentation, "Note that marketing version numbers should not
    1.22 +      * be used to check for language features, as different vendors use different
    1.23 +      * numbering schemes. Instead, use the feature checking macros."
    1.24 +      */
    1.25 +#    ifndef __has_extension
    1.26 +#      define __has_extension __has_feature /* compatibility, for older versions of clang */
    1.27 +#    endif
    1.28 +#    if __has_extension(is_literal) && __has_include(<type_traits>)
    1.29 +#      define MOZ_HAVE_IS_LITERAL
    1.30 +#    endif
    1.31 +#  elif defined(__GNUC__)
    1.32 +#    if defined(__GXX_EXPERIMENTAL_CXX0X__)
    1.33 +#      if MOZ_GCC_VERSION_AT_LEAST(4, 6, 0)
    1.34 +#        define MOZ_HAVE_IS_LITERAL
    1.35 +#      endif
    1.36 +#    endif
    1.37 +#  elif defined(_MSC_VER)
    1.38 +#    if _MSC_VER >= 1700
    1.39 +#      define MOZ_HAVE_IS_LITERAL
    1.40 +#    endif
    1.41 +#  endif
    1.42 +#endif
    1.43 +
    1.44 +#ifdef MOZ_HAVE_IS_LITERAL
    1.45 +#include <type_traits>
    1.46 +template<typename T>
    1.47 +void
    1.48 +RequireLiteralType()
    1.49 +{
    1.50 +  static_assert(std::is_literal_type<T>::value, "Expected a literal type");
    1.51 +}
    1.52 +#else // not MOZ_HAVE_IS_LITERAL
    1.53 +template<typename T>
    1.54 +void
    1.55 +RequireLiteralType()
    1.56 +{
    1.57 +}
    1.58 +#endif
    1.59 +
    1.60 +template<typename T>
    1.61 +void
    1.62 +RequireLiteralType(const T&)
    1.63 +{
    1.64 +  RequireLiteralType<T>();
    1.65 +}
    1.66 +
    1.67 +MOZ_BEGIN_ENUM_CLASS(AutoEnum)
    1.68 +  A,
    1.69 +  B = -3,
    1.70 +  C
    1.71 +MOZ_END_ENUM_CLASS(AutoEnum)
    1.72 +
    1.73 +MOZ_BEGIN_ENUM_CLASS(CharEnum, char)
    1.74 +  A,
    1.75 +  B = 3,
    1.76 +  C
    1.77 +MOZ_END_ENUM_CLASS(CharEnum)
    1.78 +
    1.79 +MOZ_BEGIN_ENUM_CLASS(AutoEnumBitField)
    1.80 +  A = 0x10,
    1.81 +  B = 0x20,
    1.82 +  C
    1.83 +MOZ_END_ENUM_CLASS(AutoEnumBitField)
    1.84 +
    1.85 +MOZ_BEGIN_ENUM_CLASS(CharEnumBitField, char)
    1.86 +  A = 0x10,
    1.87 +  B,
    1.88 +  C = 0x40
    1.89 +MOZ_END_ENUM_CLASS(CharEnumBitField)
    1.90 +
    1.91 +struct Nested
    1.92 +{
    1.93 +  MOZ_BEGIN_NESTED_ENUM_CLASS(AutoEnum)
    1.94 +    A,
    1.95 +    B,
    1.96 +    C = -1
    1.97 +  MOZ_END_NESTED_ENUM_CLASS(AutoEnum)
    1.98 +
    1.99 +  MOZ_BEGIN_NESTED_ENUM_CLASS(CharEnum, char)
   1.100 +    A = 4,
   1.101 +    B,
   1.102 +    C = 1
   1.103 +  MOZ_END_NESTED_ENUM_CLASS(CharEnum)
   1.104 +
   1.105 +  MOZ_BEGIN_NESTED_ENUM_CLASS(AutoEnumBitField)
   1.106 +    A,
   1.107 +    B = 0x20,
   1.108 +    C
   1.109 +  MOZ_END_NESTED_ENUM_CLASS(AutoEnumBitField)
   1.110 +
   1.111 +  MOZ_BEGIN_NESTED_ENUM_CLASS(CharEnumBitField, char)
   1.112 +    A = 1,
   1.113 +    B = 1,
   1.114 +    C = 1
   1.115 +  MOZ_END_NESTED_ENUM_CLASS(CharEnumBitField)
   1.116 +};
   1.117 +
   1.118 +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(AutoEnumBitField)
   1.119 +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(CharEnumBitField)
   1.120 +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Nested::AutoEnumBitField)
   1.121 +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Nested::CharEnumBitField)
   1.122 +
   1.123 +#define MAKE_STANDARD_BITFIELD_FOR_TYPE(IntType)                   \
   1.124 +  MOZ_BEGIN_ENUM_CLASS(BitFieldFor_##IntType, IntType)             \
   1.125 +    A = 1,                                                         \
   1.126 +    B = 2,                                                         \
   1.127 +    C = 4,                                                         \
   1.128 +  MOZ_END_ENUM_CLASS(BitFieldFor_##IntType)                        \
   1.129 +  MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(BitFieldFor_##IntType)
   1.130 +
   1.131 +MAKE_STANDARD_BITFIELD_FOR_TYPE(int8_t)
   1.132 +MAKE_STANDARD_BITFIELD_FOR_TYPE(uint8_t)
   1.133 +MAKE_STANDARD_BITFIELD_FOR_TYPE(int16_t)
   1.134 +MAKE_STANDARD_BITFIELD_FOR_TYPE(uint16_t)
   1.135 +MAKE_STANDARD_BITFIELD_FOR_TYPE(int32_t)
   1.136 +MAKE_STANDARD_BITFIELD_FOR_TYPE(uint32_t)
   1.137 +MAKE_STANDARD_BITFIELD_FOR_TYPE(int64_t)
   1.138 +MAKE_STANDARD_BITFIELD_FOR_TYPE(uint64_t)
   1.139 +MAKE_STANDARD_BITFIELD_FOR_TYPE(char)
   1.140 +typedef signed char signed_char;
   1.141 +MAKE_STANDARD_BITFIELD_FOR_TYPE(signed_char)
   1.142 +typedef unsigned char unsigned_char;
   1.143 +MAKE_STANDARD_BITFIELD_FOR_TYPE(unsigned_char)
   1.144 +MAKE_STANDARD_BITFIELD_FOR_TYPE(short)
   1.145 +typedef unsigned short unsigned_short;
   1.146 +MAKE_STANDARD_BITFIELD_FOR_TYPE(unsigned_short)
   1.147 +MAKE_STANDARD_BITFIELD_FOR_TYPE(int)
   1.148 +typedef unsigned int unsigned_int;
   1.149 +MAKE_STANDARD_BITFIELD_FOR_TYPE(unsigned_int)
   1.150 +MAKE_STANDARD_BITFIELD_FOR_TYPE(long)
   1.151 +typedef unsigned long unsigned_long;
   1.152 +MAKE_STANDARD_BITFIELD_FOR_TYPE(unsigned_long)
   1.153 +typedef long long long_long;
   1.154 +MAKE_STANDARD_BITFIELD_FOR_TYPE(long_long)
   1.155 +typedef unsigned long long unsigned_long_long;
   1.156 +MAKE_STANDARD_BITFIELD_FOR_TYPE(unsigned_long_long)
   1.157 +
   1.158 +#undef MAKE_STANDARD_BITFIELD_FOR_TYPE
   1.159 +
   1.160 +template<typename T>
   1.161 +void
   1.162 +TestNonConvertibilityForOneType()
   1.163 +{
   1.164 +  using mozilla::IsConvertible;
   1.165 +
   1.166 +#if defined(MOZ_HAVE_CXX11_STRONG_ENUMS) && defined(MOZ_HAVE_EXPLICIT_CONVERSION)
   1.167 +  static_assert(!IsConvertible<T, bool>::value, "should not be convertible");
   1.168 +  static_assert(!IsConvertible<T, int>::value, "should not be convertible");
   1.169 +  static_assert(!IsConvertible<T, uint64_t>::value, "should not be convertible");
   1.170 +#endif
   1.171 +
   1.172 +  static_assert(!IsConvertible<bool, T>::value, "should not be convertible");
   1.173 +  static_assert(!IsConvertible<int, T>::value, "should not be convertible");
   1.174 +  static_assert(!IsConvertible<uint64_t, T>::value, "should not be convertible");
   1.175 +}
   1.176 +
   1.177 +template<typename TypedEnum>
   1.178 +void
   1.179 +TestTypedEnumBasics()
   1.180 +{
   1.181 +  const TypedEnum a = TypedEnum::A;
   1.182 +  int unused = int(a);
   1.183 +  (void) unused;
   1.184 +  RequireLiteralType(TypedEnum::A);
   1.185 +  RequireLiteralType(a);
   1.186 +  TestNonConvertibilityForOneType<TypedEnum>();
   1.187 +}
   1.188 +
   1.189 +// Op wraps a bitwise binary operator, passed as a char template parameter,
   1.190 +// and applies it to its arguments (t1, t2). For example,
   1.191 +//
   1.192 +//   Op<'|'>(t1, t2)
   1.193 +//
   1.194 +// is the same as
   1.195 +//
   1.196 +//   t1 | t2.
   1.197 +//
   1.198 +template<char o, typename T1, typename T2>
   1.199 +auto Op(const T1& t1, const T2& t2)
   1.200 +  -> decltype(t1 | t2) // See the static_assert's below --- the return type
   1.201 +                       // depends solely on the operands type, not on the
   1.202 +                       // choice of operation.
   1.203 +{
   1.204 +  using mozilla::IsSame;
   1.205 +  static_assert(IsSame<decltype(t1 | t2), decltype(t1 & t2)>::value,
   1.206 +                "binary ops should have the same result type");
   1.207 +  static_assert(IsSame<decltype(t1 | t2), decltype(t1 ^ t2)>::value,
   1.208 +                "binary ops should have the same result type");
   1.209 +
   1.210 +  static_assert(o == '|' ||
   1.211 +                o == '&' ||
   1.212 +                o == '^', "unexpected operator character");
   1.213 +
   1.214 +  return o == '|' ? t1 | t2
   1.215 +       : o == '&' ? t1 & t2
   1.216 +                  : t1 ^ t2;
   1.217 +}
   1.218 +
   1.219 +// OpAssign wraps a bitwise binary operator, passed as a char template
   1.220 +// parameter, and applies the corresponding compound-assignment operator to its
   1.221 +// arguments (t1, t2). For example,
   1.222 +//
   1.223 +//   OpAssign<'|'>(t1, t2)
   1.224 +//
   1.225 +// is the same as
   1.226 +//
   1.227 +//   t1 |= t2.
   1.228 +//
   1.229 +template<char o, typename T1, typename T2>
   1.230 +T1& OpAssign(T1& t1, const T2& t2)
   1.231 +{
   1.232 +  static_assert(o == '|' ||
   1.233 +                o == '&' ||
   1.234 +                o == '^', "unexpected operator character");
   1.235 +
   1.236 +  switch (o) {
   1.237 +    case '|': return t1 |= t2;
   1.238 +    case '&': return t1 &= t2;
   1.239 +    case '^': return t1 ^= t2;
   1.240 +    default: MOZ_CRASH();
   1.241 +  }
   1.242 +}
   1.243 +
   1.244 +// Tests a single binary bitwise operator, using a single set of three operands.
   1.245 +// The operations tested are:
   1.246 +//
   1.247 +//   result = t1 Op t2;
   1.248 +//   result Op= t3;
   1.249 +//
   1.250 +// Where Op is the operator specified by the char template parameter 'o' and can
   1.251 +// be any of '|', '&', '^'.
   1.252 +//
   1.253 +// Note that the operands t1, t2, t3 are intentionally passed with free types
   1.254 +// (separate template parameters for each) because their type may actually be
   1.255 +// different from TypedEnum:
   1.256 +//   1) Their type could be CastableTypedEnumResult<TypedEnum> if they are
   1.257 +//      the result of a bitwise operation themselves;
   1.258 +//   2) In the non-c++11 legacy path, the type of enum values is also
   1.259 +//      different from TypedEnum.
   1.260 +//
   1.261 +template<typename TypedEnum, char o, typename T1, typename T2, typename T3>
   1.262 +void TestBinOp(const T1& t1, const T2& t2, const T3& t3)
   1.263 +{
   1.264 +  typedef typename mozilla::detail::UnsignedIntegerTypeForEnum<TypedEnum>::Type
   1.265 +          UnsignedIntegerType;
   1.266 +
   1.267 +  // Part 1:
   1.268 +  // Test the bitwise binary operator i.e.
   1.269 +  //   result = t1 Op t2;
   1.270 +  auto result = Op<o>(t1, t2);
   1.271 +
   1.272 +  typedef decltype(result) ResultType;
   1.273 +
   1.274 +  RequireLiteralType<ResultType>();
   1.275 +  TestNonConvertibilityForOneType<ResultType>();
   1.276 +
   1.277 +  UnsignedIntegerType unsignedIntegerResult
   1.278 +    = Op<o>(UnsignedIntegerType(t1), UnsignedIntegerType(t2));
   1.279 +
   1.280 +  MOZ_RELEASE_ASSERT(unsignedIntegerResult == UnsignedIntegerType(result));
   1.281 +  MOZ_RELEASE_ASSERT(TypedEnum(unsignedIntegerResult) == TypedEnum(result));
   1.282 +  MOZ_RELEASE_ASSERT((!unsignedIntegerResult) == (!result));
   1.283 +  MOZ_RELEASE_ASSERT((!!unsignedIntegerResult) == (!!result));
   1.284 +  MOZ_RELEASE_ASSERT(bool(unsignedIntegerResult) == bool(result));
   1.285 +
   1.286 +  // Part 2:
   1.287 +  // Test the compound-assignment operator, i.e.
   1.288 +  //   result Op= t3;
   1.289 +  TypedEnum newResult = result;
   1.290 +  OpAssign<o>(newResult, t3);
   1.291 +  UnsignedIntegerType unsignedIntegerNewResult = unsignedIntegerResult;
   1.292 +  OpAssign<o>(unsignedIntegerNewResult, UnsignedIntegerType(t3));
   1.293 +  MOZ_RELEASE_ASSERT(TypedEnum(unsignedIntegerNewResult) == newResult);
   1.294 +
   1.295 +  // Part 3:
   1.296 +  // Test additional boolean operators that we unfortunately had to add to
   1.297 +  // CastableTypedEnumResult at some point to please some compiler,
   1.298 +  // even though bool convertibility should have been enough.
   1.299 +  MOZ_RELEASE_ASSERT(result == TypedEnum(result));
   1.300 +  MOZ_RELEASE_ASSERT(!(result != TypedEnum(result)));
   1.301 +  MOZ_RELEASE_ASSERT((result && true) == bool(result));
   1.302 +  MOZ_RELEASE_ASSERT((result && false) == false);
   1.303 +  MOZ_RELEASE_ASSERT((true && result) == bool(result));
   1.304 +  MOZ_RELEASE_ASSERT((false && result && false) == false);
   1.305 +  MOZ_RELEASE_ASSERT((result || false) == bool(result));
   1.306 +  MOZ_RELEASE_ASSERT((result || true) == true);
   1.307 +  MOZ_RELEASE_ASSERT((false || result) == bool(result));
   1.308 +  MOZ_RELEASE_ASSERT((true || result) == true);
   1.309 +}
   1.310 +
   1.311 +// Similar to TestBinOp but testing the unary ~ operator.
   1.312 +template<typename TypedEnum, typename T>
   1.313 +void TestTilde(const T& t)
   1.314 +{
   1.315 +  typedef typename mozilla::detail::UnsignedIntegerTypeForEnum<TypedEnum>::Type
   1.316 +          UnsignedIntegerType;
   1.317 +
   1.318 +  auto result = ~t;
   1.319 +
   1.320 +  typedef decltype(result) ResultType;
   1.321 +
   1.322 +  RequireLiteralType<ResultType>();
   1.323 +  TestNonConvertibilityForOneType<ResultType>();
   1.324 +
   1.325 +  UnsignedIntegerType unsignedIntegerResult = ~(UnsignedIntegerType(t));
   1.326 +
   1.327 +  MOZ_RELEASE_ASSERT(unsignedIntegerResult == UnsignedIntegerType(result));
   1.328 +  MOZ_RELEASE_ASSERT(TypedEnum(unsignedIntegerResult) == TypedEnum(result));
   1.329 +  MOZ_RELEASE_ASSERT((!unsignedIntegerResult) == (!result));
   1.330 +  MOZ_RELEASE_ASSERT((!!unsignedIntegerResult) == (!!result));
   1.331 +  MOZ_RELEASE_ASSERT(bool(unsignedIntegerResult) == bool(result));
   1.332 +}
   1.333 +
   1.334 +// Helper dispatching a given triple of operands to all operator-specific
   1.335 +// testing functions.
   1.336 +template<typename TypedEnum, typename T1, typename T2, typename T3>
   1.337 +void TestAllOpsForGivenOperands(const T1& t1, const T2& t2, const T3& t3)
   1.338 +{
   1.339 +  TestBinOp<TypedEnum, '|'>(t1, t2, t3);
   1.340 +  TestBinOp<TypedEnum, '&'>(t1, t2, t3);
   1.341 +  TestBinOp<TypedEnum, '^'>(t1, t2, t3);
   1.342 +  TestTilde<TypedEnum>(t1);
   1.343 +}
   1.344 +
   1.345 +// Helper building various triples of operands using a given operator,
   1.346 +// and testing all operators with them.
   1.347 +template<typename TypedEnum, char o>
   1.348 +void TestAllOpsForOperandsBuiltUsingGivenOp()
   1.349 +{
   1.350 +  // The type of enum values like TypedEnum::A may be different from
   1.351 +  // TypedEnum. That is the case in the legacy non-C++11 path. We want to
   1.352 +  // ensure good test coverage even when these two types are distinct.
   1.353 +  // To that effect, we have both 'auto' typed variables, preserving the
   1.354 +  // original type of enum values, and 'plain' typed variables, that
   1.355 +  // are plain TypedEnum's.
   1.356 +
   1.357 +  const TypedEnum a_plain = TypedEnum::A;
   1.358 +  const TypedEnum b_plain = TypedEnum::B;
   1.359 +  const TypedEnum c_plain = TypedEnum::C;
   1.360 +
   1.361 +  auto a_auto = TypedEnum::A;
   1.362 +  auto b_auto = TypedEnum::B;
   1.363 +  auto c_auto = TypedEnum::C;
   1.364 +
   1.365 +  auto ab_plain = Op<o>(a_plain, b_plain);
   1.366 +  auto bc_plain = Op<o>(b_plain, c_plain);
   1.367 +  auto ab_auto = Op<o>(a_auto, b_auto);
   1.368 +  auto bc_auto = Op<o>(b_auto, c_auto);
   1.369 +
   1.370 +  // On each row below, we pass a triple of operands. Keep in mind that this
   1.371 +  // is going to be received as (t1, t2, t3) and the actual tests performed
   1.372 +  // will be of the form
   1.373 +  //
   1.374 +  //   result = t1 Op t2;
   1.375 +  //   result Op= t3;
   1.376 +  //
   1.377 +  // For this reason, we carefully ensure that the values of (t1, t2)
   1.378 +  // systematically cover all types of such pairs; to limit complexity,
   1.379 +  // we are not so careful with t3, and we just try to pass t3's
   1.380 +  // that may lead to nontrivial bitwise operations.
   1.381 +  TestAllOpsForGivenOperands<TypedEnum>(a_plain,  b_plain,  c_plain);
   1.382 +  TestAllOpsForGivenOperands<TypedEnum>(a_plain,  bc_plain, b_auto);
   1.383 +  TestAllOpsForGivenOperands<TypedEnum>(ab_plain, c_plain,  a_plain);
   1.384 +  TestAllOpsForGivenOperands<TypedEnum>(ab_plain, bc_plain, a_auto);
   1.385 +
   1.386 +  TestAllOpsForGivenOperands<TypedEnum>(a_plain,  b_auto,   c_plain);
   1.387 +  TestAllOpsForGivenOperands<TypedEnum>(a_plain,  bc_auto,  b_auto);
   1.388 +  TestAllOpsForGivenOperands<TypedEnum>(ab_plain, c_auto,   a_plain);
   1.389 +  TestAllOpsForGivenOperands<TypedEnum>(ab_plain, bc_auto,  a_auto);
   1.390 +
   1.391 +  TestAllOpsForGivenOperands<TypedEnum>(a_auto,   b_plain,  c_plain);
   1.392 +  TestAllOpsForGivenOperands<TypedEnum>(a_auto,   bc_plain, b_auto);
   1.393 +  TestAllOpsForGivenOperands<TypedEnum>(ab_auto,  c_plain,  a_plain);
   1.394 +  TestAllOpsForGivenOperands<TypedEnum>(ab_auto,  bc_plain, a_auto);
   1.395 +
   1.396 +  TestAllOpsForGivenOperands<TypedEnum>(a_auto,   b_auto,   c_plain);
   1.397 +  TestAllOpsForGivenOperands<TypedEnum>(a_auto,   bc_auto,  b_auto);
   1.398 +  TestAllOpsForGivenOperands<TypedEnum>(ab_auto,  c_auto,   a_plain);
   1.399 +  TestAllOpsForGivenOperands<TypedEnum>(ab_auto,  bc_auto,  a_auto);
   1.400 +}
   1.401 +
   1.402 +// Tests all bitwise operations on a given TypedEnum bitfield.
   1.403 +template<typename TypedEnum>
   1.404 +void
   1.405 +TestTypedEnumBitField()
   1.406 +{
   1.407 +  TestTypedEnumBasics<TypedEnum>();
   1.408 +
   1.409 +  TestAllOpsForOperandsBuiltUsingGivenOp<TypedEnum, '|'>();
   1.410 +  TestAllOpsForOperandsBuiltUsingGivenOp<TypedEnum, '&'>();
   1.411 +  TestAllOpsForOperandsBuiltUsingGivenOp<TypedEnum, '^'>();
   1.412 +}
   1.413 +
   1.414 +// Checks that enum bitwise expressions have the same non-convertibility properties as
   1.415 +// c++11 enum classes do, i.e. not implicitly convertible to anything
   1.416 +// (though *explicitly* convertible).
   1.417 +void TestNoConversionsBetweenUnrelatedTypes()
   1.418 +{
   1.419 +  using mozilla::IsConvertible;
   1.420 +
   1.421 +  // Two typed enum classes having the same underlying integer type, to ensure that
   1.422 +  // we would catch bugs accidentally allowing conversions in that case.
   1.423 +  typedef CharEnumBitField T1;
   1.424 +  typedef Nested::CharEnumBitField T2;
   1.425 +
   1.426 +  static_assert(!IsConvertible<T1, T2>::value,
   1.427 +                "should not be convertible");
   1.428 +  static_assert(!IsConvertible<T1, decltype(T2::A)>::value,
   1.429 +                "should not be convertible");
   1.430 +  static_assert(!IsConvertible<T1, decltype(T2::A | T2::B)>::value,
   1.431 +                "should not be convertible");
   1.432 +
   1.433 +  static_assert(!IsConvertible<decltype(T1::A), T2>::value,
   1.434 +                "should not be convertible");
   1.435 +  static_assert(!IsConvertible<decltype(T1::A), decltype(T2::A)>::value,
   1.436 +                "should not be convertible");
   1.437 +  static_assert(!IsConvertible<decltype(T1::A), decltype(T2::A | T2::B)>::value,
   1.438 +                "should not be convertible");
   1.439 +
   1.440 +  // The following are #ifdef MOZ_HAVE_EXPLICIT_CONVERSION because
   1.441 +  // without support for explicit conversion operators, we can't easily have these
   1.442 +  // bad conversions completely removed. They still do fail to compile in practice,
   1.443 +  // but not in a way that we can static_assert on.
   1.444 +#ifdef MOZ_HAVE_EXPLICIT_CONVERSION
   1.445 +  static_assert(!IsConvertible<decltype(T1::A | T1::B), T2>::value,
   1.446 +                "should not be convertible");
   1.447 +  static_assert(!IsConvertible<decltype(T1::A | T1::B), decltype(T2::A)>::value,
   1.448 +                "should not be convertible");
   1.449 +  static_assert(!IsConvertible<decltype(T1::A | T1::B), decltype(T2::A | T2::B)>::value,
   1.450 +                "should not be convertible");
   1.451 +#endif
   1.452 +}
   1.453 +
   1.454 +MOZ_BEGIN_ENUM_CLASS(Int8EnumWithHighBits, int8_t)
   1.455 +  A = 0x20,
   1.456 +  B = 0x40
   1.457 +MOZ_END_ENUM_CLASS(Int8EnumWithHighBits)
   1.458 +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Int8EnumWithHighBits)
   1.459 +
   1.460 +MOZ_BEGIN_ENUM_CLASS(Uint8EnumWithHighBits, uint8_t)
   1.461 +  A = 0x40,
   1.462 +  B = 0x80
   1.463 +MOZ_END_ENUM_CLASS(Uint8EnumWithHighBits)
   1.464 +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Uint8EnumWithHighBits)
   1.465 +
   1.466 +MOZ_BEGIN_ENUM_CLASS(Int16EnumWithHighBits, int16_t)
   1.467 +  A = 0x2000,
   1.468 +  B = 0x4000
   1.469 +MOZ_END_ENUM_CLASS(Int16EnumWithHighBits)
   1.470 +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Int16EnumWithHighBits)
   1.471 +
   1.472 +MOZ_BEGIN_ENUM_CLASS(Uint16EnumWithHighBits, uint16_t)
   1.473 +  A = 0x4000,
   1.474 +  B = 0x8000
   1.475 +MOZ_END_ENUM_CLASS(Uint16EnumWithHighBits)
   1.476 +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Uint16EnumWithHighBits)
   1.477 +
   1.478 +MOZ_BEGIN_ENUM_CLASS(Int32EnumWithHighBits, int32_t)
   1.479 +  A = 0x20000000,
   1.480 +  B = 0x40000000
   1.481 +MOZ_END_ENUM_CLASS(Int32EnumWithHighBits)
   1.482 +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Int32EnumWithHighBits)
   1.483 +
   1.484 +MOZ_BEGIN_ENUM_CLASS(Uint32EnumWithHighBits, uint32_t)
   1.485 +  A = 0x40000000u,
   1.486 +  B = 0x80000000u
   1.487 +MOZ_END_ENUM_CLASS(Uint32EnumWithHighBits)
   1.488 +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Uint32EnumWithHighBits)
   1.489 +
   1.490 +MOZ_BEGIN_ENUM_CLASS(Int64EnumWithHighBits, int64_t)
   1.491 +  A = 0x2000000000000000ll,
   1.492 +  B = 0x4000000000000000ll
   1.493 +MOZ_END_ENUM_CLASS(Int64EnumWithHighBits)
   1.494 +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Int64EnumWithHighBits)
   1.495 +
   1.496 +MOZ_BEGIN_ENUM_CLASS(Uint64EnumWithHighBits, uint64_t)
   1.497 +  A = 0x4000000000000000ull,
   1.498 +  B = 0x8000000000000000ull
   1.499 +MOZ_END_ENUM_CLASS(Uint64EnumWithHighBits)
   1.500 +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Uint64EnumWithHighBits)
   1.501 +
   1.502 +// Checks that we don't accidentally truncate high bits by coercing to the wrong
   1.503 +// integer type internally when implementing bitwise ops.
   1.504 +template<typename EnumType, typename IntType>
   1.505 +void TestIsNotTruncated()
   1.506 +{
   1.507 +  EnumType a = EnumType::A;
   1.508 +  EnumType b = EnumType::B;
   1.509 +  MOZ_RELEASE_ASSERT(IntType(a));
   1.510 +  MOZ_RELEASE_ASSERT(IntType(b));
   1.511 +  MOZ_RELEASE_ASSERT(a | EnumType::B);
   1.512 +  MOZ_RELEASE_ASSERT(a | b);
   1.513 +  MOZ_RELEASE_ASSERT(EnumType::A | EnumType::B);
   1.514 +  EnumType c = EnumType::A | EnumType::B;
   1.515 +  MOZ_RELEASE_ASSERT(IntType(c));
   1.516 +  MOZ_RELEASE_ASSERT(c & c);
   1.517 +  MOZ_RELEASE_ASSERT(c | c);
   1.518 +  MOZ_RELEASE_ASSERT(c == (EnumType::A | EnumType::B));
   1.519 +  MOZ_RELEASE_ASSERT(a != (EnumType::A | EnumType::B));
   1.520 +  MOZ_RELEASE_ASSERT(b != (EnumType::A | EnumType::B));
   1.521 +  MOZ_RELEASE_ASSERT(c & EnumType::A);
   1.522 +  MOZ_RELEASE_ASSERT(c & EnumType::B);
   1.523 +  EnumType d = EnumType::A;
   1.524 +  d |= EnumType::B;
   1.525 +  MOZ_RELEASE_ASSERT(d == c);
   1.526 +}
   1.527 +
   1.528 +int
   1.529 +main()
   1.530 +{
   1.531 +  TestTypedEnumBasics<AutoEnum>();
   1.532 +  TestTypedEnumBasics<CharEnum>();
   1.533 +  TestTypedEnumBasics<Nested::AutoEnum>();
   1.534 +  TestTypedEnumBasics<Nested::CharEnum>();
   1.535 +
   1.536 +  TestTypedEnumBitField<AutoEnumBitField>();
   1.537 +  TestTypedEnumBitField<CharEnumBitField>();
   1.538 +  TestTypedEnumBitField<Nested::AutoEnumBitField>();
   1.539 +  TestTypedEnumBitField<Nested::CharEnumBitField>();
   1.540 +
   1.541 +  TestTypedEnumBitField<BitFieldFor_uint8_t>();
   1.542 +  TestTypedEnumBitField<BitFieldFor_int8_t>();
   1.543 +  TestTypedEnumBitField<BitFieldFor_uint16_t>();
   1.544 +  TestTypedEnumBitField<BitFieldFor_int16_t>();
   1.545 +  TestTypedEnumBitField<BitFieldFor_uint32_t>();
   1.546 +  TestTypedEnumBitField<BitFieldFor_int32_t>();
   1.547 +  TestTypedEnumBitField<BitFieldFor_uint64_t>();
   1.548 +  TestTypedEnumBitField<BitFieldFor_int64_t>();
   1.549 +  TestTypedEnumBitField<BitFieldFor_char>();
   1.550 +  TestTypedEnumBitField<BitFieldFor_signed_char>();
   1.551 +  TestTypedEnumBitField<BitFieldFor_unsigned_char>();
   1.552 +  TestTypedEnumBitField<BitFieldFor_short>();
   1.553 +  TestTypedEnumBitField<BitFieldFor_unsigned_short>();
   1.554 +  TestTypedEnumBitField<BitFieldFor_int>();
   1.555 +  TestTypedEnumBitField<BitFieldFor_unsigned_int>();
   1.556 +  TestTypedEnumBitField<BitFieldFor_long>();
   1.557 +  TestTypedEnumBitField<BitFieldFor_unsigned_long>();
   1.558 +  TestTypedEnumBitField<BitFieldFor_long_long>();
   1.559 +  TestTypedEnumBitField<BitFieldFor_unsigned_long_long>();
   1.560 +
   1.561 +  TestNoConversionsBetweenUnrelatedTypes();
   1.562 +
   1.563 +  TestIsNotTruncated<Int8EnumWithHighBits, int8_t>();
   1.564 +  TestIsNotTruncated<Int16EnumWithHighBits, int16_t>();
   1.565 +  TestIsNotTruncated<Int32EnumWithHighBits, int32_t>();
   1.566 +  TestIsNotTruncated<Int64EnumWithHighBits, int64_t>();
   1.567 +  TestIsNotTruncated<Uint8EnumWithHighBits, uint8_t>();
   1.568 +  TestIsNotTruncated<Uint16EnumWithHighBits, uint16_t>();
   1.569 +  TestIsNotTruncated<Uint32EnumWithHighBits, uint32_t>();
   1.570 +  TestIsNotTruncated<Uint64EnumWithHighBits, uint64_t>();
   1.571 +
   1.572 +  return 0;
   1.573 +}

mercurial