DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Mercurial (b6057e17f856)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570
/* 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/Attributes.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 = -3,
  C
MOZ_END_ENUM_CLASS(AutoEnum)

MOZ_BEGIN_ENUM_CLASS(CharEnum, char)
  A,
  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)
    A,
    B,
    C = -1
  MOZ_END_NESTED_ENUM_CLASS(AutoEnum)

  MOZ_BEGIN_NESTED_ENUM_CLASS(CharEnum, char)
    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)
};

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;

#if defined(MOZ_HAVE_CXX11_STRONG_ENUMS) && defined(MOZ_HAVE_EXPLICIT_CONVERSION)
  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(TypedEnum(unsignedIntegerResult) == 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(TypedEnum(unsignedIntegerNewResult) == 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(TypedEnum(unsignedIntegerResult) == 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;
}