Blame mfbt/tests/TestTypedEnum.cpp

Packit f0b94e
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
Packit f0b94e
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
Packit f0b94e
/* This Source Code Form is subject to the terms of the Mozilla Public
Packit f0b94e
 * License, v. 2.0. If a copy of the MPL was not distributed with this
Packit f0b94e
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Packit f0b94e
Packit f0b94e
#include "mozilla/Assertions.h"
Packit f0b94e
#include "mozilla/Attributes.h"
Packit f0b94e
#include "mozilla/TypedEnumBits.h"
Packit f0b94e
Packit f0b94e
#include <stdint.h>
Packit f0b94e
Packit f0b94e
// A rough feature check for is_literal_type. Not very carefully checked.
Packit f0b94e
// Feel free to amend as needed.
Packit f0b94e
// We leave ANDROID out because it's using stlport which doesn't have std::is_literal_type.
Packit f0b94e
#if __cplusplus >= 201103L && !defined(ANDROID)
Packit f0b94e
#  if defined(__clang__)
Packit f0b94e
     /*
Packit f0b94e
      * Per Clang documentation, "Note that marketing version numbers should not
Packit f0b94e
      * be used to check for language features, as different vendors use different
Packit f0b94e
      * numbering schemes. Instead, use the feature checking macros."
Packit f0b94e
      */
Packit f0b94e
#    ifndef __has_extension
Packit f0b94e
#      define __has_extension __has_feature /* compatibility, for older versions of clang */
Packit f0b94e
#    endif
Packit f0b94e
#    if __has_extension(is_literal) && __has_include(<type_traits>)
Packit f0b94e
#      define MOZ_HAVE_IS_LITERAL
Packit f0b94e
#    endif
Packit f0b94e
#  elif defined(__GNUC__) || defined(_MSC_VER)
Packit f0b94e
#    define MOZ_HAVE_IS_LITERAL
Packit f0b94e
#  endif
Packit f0b94e
#endif
Packit f0b94e
Packit f0b94e
#if defined(MOZ_HAVE_IS_LITERAL) && defined(MOZ_HAVE_CXX11_CONSTEXPR)
Packit f0b94e
#include <type_traits>
Packit f0b94e
template<typename T>
Packit f0b94e
void
Packit f0b94e
RequireLiteralType()
Packit f0b94e
{
Packit f0b94e
  static_assert(std::is_literal_type<T>::value, "Expected a literal type");
Packit f0b94e
}
Packit f0b94e
#else // not MOZ_HAVE_IS_LITERAL
Packit f0b94e
template<typename T>
Packit f0b94e
void
Packit f0b94e
RequireLiteralType()
Packit f0b94e
{
Packit f0b94e
}
Packit f0b94e
#endif
Packit f0b94e
Packit f0b94e
template<typename T>
Packit f0b94e
void
Packit f0b94e
RequireLiteralType(const T&)
Packit f0b94e
{
Packit f0b94e
  RequireLiteralType<T>();
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
enum class AutoEnum {
Packit f0b94e
  A,
Packit f0b94e
  B = -3,
Packit f0b94e
  C
Packit f0b94e
};
Packit f0b94e
Packit f0b94e
enum class CharEnum : char {
Packit f0b94e
  A,
Packit f0b94e
  B = 3,
Packit f0b94e
  C
Packit f0b94e
};
Packit f0b94e
Packit f0b94e
enum class AutoEnumBitField {
Packit f0b94e
  A = 0x10,
Packit f0b94e
  B = 0x20,
Packit f0b94e
  C
Packit f0b94e
};
Packit f0b94e
Packit f0b94e
enum class CharEnumBitField : char {
Packit f0b94e
  A = 0x10,
Packit f0b94e
  B,
Packit f0b94e
  C = 0x40
Packit f0b94e
};
Packit f0b94e
Packit f0b94e
struct Nested
Packit f0b94e
{
Packit f0b94e
  enum class AutoEnum {
Packit f0b94e
    A,
Packit f0b94e
    B,
Packit f0b94e
    C = -1
Packit f0b94e
  };
Packit f0b94e
Packit f0b94e
  enum class CharEnum : char {
Packit f0b94e
    A = 4,
Packit f0b94e
    B,
Packit f0b94e
    C = 1
Packit f0b94e
  };
Packit f0b94e
Packit f0b94e
  enum class AutoEnumBitField {
Packit f0b94e
    A,
Packit f0b94e
    B = 0x20,
Packit f0b94e
    C
Packit f0b94e
  };
Packit f0b94e
Packit f0b94e
  enum class CharEnumBitField : char {
Packit f0b94e
    A = 1,
Packit f0b94e
    B = 1,
Packit f0b94e
    C = 1
Packit f0b94e
  };
Packit f0b94e
};
Packit f0b94e
Packit f0b94e
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(AutoEnumBitField)
Packit f0b94e
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(CharEnumBitField)
Packit f0b94e
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Nested::AutoEnumBitField)
Packit f0b94e
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Nested::CharEnumBitField)
Packit f0b94e
Packit f0b94e
#define MAKE_STANDARD_BITFIELD_FOR_TYPE(IntType)                   \
Packit f0b94e
  enum class BitFieldFor_##IntType : IntType {                     \
Packit f0b94e
    A = 1,                                                         \
Packit f0b94e
    B = 2,                                                         \
Packit f0b94e
    C = 4,                                                         \
Packit f0b94e
  };                                                               \
Packit f0b94e
  MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(BitFieldFor_##IntType)
Packit f0b94e
Packit f0b94e
MAKE_STANDARD_BITFIELD_FOR_TYPE(int8_t)
Packit f0b94e
MAKE_STANDARD_BITFIELD_FOR_TYPE(uint8_t)
Packit f0b94e
MAKE_STANDARD_BITFIELD_FOR_TYPE(int16_t)
Packit f0b94e
MAKE_STANDARD_BITFIELD_FOR_TYPE(uint16_t)
Packit f0b94e
MAKE_STANDARD_BITFIELD_FOR_TYPE(int32_t)
Packit f0b94e
MAKE_STANDARD_BITFIELD_FOR_TYPE(uint32_t)
Packit f0b94e
MAKE_STANDARD_BITFIELD_FOR_TYPE(int64_t)
Packit f0b94e
MAKE_STANDARD_BITFIELD_FOR_TYPE(uint64_t)
Packit f0b94e
MAKE_STANDARD_BITFIELD_FOR_TYPE(char)
Packit f0b94e
typedef signed char signed_char;
Packit f0b94e
MAKE_STANDARD_BITFIELD_FOR_TYPE(signed_char)
Packit f0b94e
typedef unsigned char unsigned_char;
Packit f0b94e
MAKE_STANDARD_BITFIELD_FOR_TYPE(unsigned_char)
Packit f0b94e
MAKE_STANDARD_BITFIELD_FOR_TYPE(short)
Packit f0b94e
typedef unsigned short unsigned_short;
Packit f0b94e
MAKE_STANDARD_BITFIELD_FOR_TYPE(unsigned_short)
Packit f0b94e
MAKE_STANDARD_BITFIELD_FOR_TYPE(int)
Packit f0b94e
typedef unsigned int unsigned_int;
Packit f0b94e
MAKE_STANDARD_BITFIELD_FOR_TYPE(unsigned_int)
Packit f0b94e
MAKE_STANDARD_BITFIELD_FOR_TYPE(long)
Packit f0b94e
typedef unsigned long unsigned_long;
Packit f0b94e
MAKE_STANDARD_BITFIELD_FOR_TYPE(unsigned_long)
Packit f0b94e
typedef long long long_long;
Packit f0b94e
MAKE_STANDARD_BITFIELD_FOR_TYPE(long_long)
Packit f0b94e
typedef unsigned long long unsigned_long_long;
Packit f0b94e
MAKE_STANDARD_BITFIELD_FOR_TYPE(unsigned_long_long)
Packit f0b94e
Packit f0b94e
#undef MAKE_STANDARD_BITFIELD_FOR_TYPE
Packit f0b94e
Packit f0b94e
template<typename T>
Packit f0b94e
void
Packit f0b94e
TestNonConvertibilityForOneType()
Packit f0b94e
{
Packit f0b94e
  using mozilla::IsConvertible;
Packit f0b94e
Packit f0b94e
  static_assert(!IsConvertible<T, bool>::value, "should not be convertible");
Packit f0b94e
  static_assert(!IsConvertible<T, int>::value, "should not be convertible");
Packit f0b94e
  static_assert(!IsConvertible<T, uint64_t>::value, "should not be convertible");
Packit f0b94e
Packit f0b94e
  static_assert(!IsConvertible<bool, T>::value, "should not be convertible");
Packit f0b94e
  static_assert(!IsConvertible<int, T>::value, "should not be convertible");
Packit f0b94e
  static_assert(!IsConvertible<uint64_t, T>::value, "should not be convertible");
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
template<typename TypedEnum>
Packit f0b94e
void
Packit f0b94e
TestTypedEnumBasics()
Packit f0b94e
{
Packit f0b94e
  const TypedEnum a = TypedEnum::A;
Packit f0b94e
  int unused = int(a);
Packit f0b94e
  (void) unused;
Packit f0b94e
  RequireLiteralType(TypedEnum::A);
Packit f0b94e
  RequireLiteralType(a);
Packit f0b94e
  TestNonConvertibilityForOneType<TypedEnum>();
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
// Op wraps a bitwise binary operator, passed as a char template parameter,
Packit f0b94e
// and applies it to its arguments (aT1, aT2). For example,
Packit f0b94e
//
Packit f0b94e
//   Op<'|'>(aT1, aT2)
Packit f0b94e
//
Packit f0b94e
// is the same as
Packit f0b94e
//
Packit f0b94e
//   aT1 | aT2.
Packit f0b94e
//
Packit f0b94e
template<char o, typename T1, typename T2>
Packit f0b94e
auto Op(const T1& aT1, const T2& aT2)
Packit f0b94e
  -> decltype(aT1 | aT2) // See the static_assert's below --- the return type
Packit f0b94e
                         // depends solely on the operands type, not on the
Packit f0b94e
                         // choice of operation.
Packit f0b94e
{
Packit f0b94e
  using mozilla::IsSame;
Packit f0b94e
  static_assert(IsSame<decltype(aT1 | aT2), decltype(aT1 & aT2)>::value,
Packit f0b94e
                "binary ops should have the same result type");
Packit f0b94e
  static_assert(IsSame<decltype(aT1 | aT2), decltype(aT1 ^ aT2)>::value,
Packit f0b94e
                "binary ops should have the same result type");
Packit f0b94e
Packit f0b94e
  static_assert(o == '|' ||
Packit f0b94e
                o == '&' ||
Packit f0b94e
                o == '^', "unexpected operator character");
Packit f0b94e
Packit f0b94e
  return o == '|' ? aT1 | aT2
Packit f0b94e
       : o == '&' ? aT1 & aT2
Packit f0b94e
                  : aT1 ^ aT2;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
// OpAssign wraps a bitwise binary operator, passed as a char template
Packit f0b94e
// parameter, and applies the corresponding compound-assignment operator to its
Packit f0b94e
// arguments (aT1, aT2). For example,
Packit f0b94e
//
Packit f0b94e
//   OpAssign<'|'>(aT1, aT2)
Packit f0b94e
//
Packit f0b94e
// is the same as
Packit f0b94e
//
Packit f0b94e
//   aT1 |= aT2.
Packit f0b94e
//
Packit f0b94e
template<char o, typename T1, typename T2>
Packit f0b94e
T1& OpAssign(T1& aT1, const T2& aT2)
Packit f0b94e
{
Packit f0b94e
  static_assert(o == '|' ||
Packit f0b94e
                o == '&' ||
Packit f0b94e
                o == '^', "unexpected operator character");
Packit f0b94e
Packit f0b94e
  switch (o) {
Packit f0b94e
    case '|': return aT1 |= aT2;
Packit f0b94e
    case '&': return aT1 &= aT2;
Packit f0b94e
    case '^': return aT1 ^= aT2;
Packit f0b94e
    default: MOZ_CRASH();
Packit f0b94e
  }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
// Tests a single binary bitwise operator, using a single set of three operands.
Packit f0b94e
// The operations tested are:
Packit f0b94e
//
Packit f0b94e
//   result = aT1 Op aT2;
Packit f0b94e
//   result Op= aT3;
Packit f0b94e
//
Packit f0b94e
// Where Op is the operator specified by the char template parameter 'o' and
Packit f0b94e
// can be any of '|', '&', '^'.
Packit f0b94e
//
Packit f0b94e
// Note that the operands aT1, aT2, aT3 are intentionally passed with free
Packit f0b94e
// types (separate template parameters for each) because their type may
Packit f0b94e
// actually be different from TypedEnum:
Packit f0b94e
//
Packit f0b94e
//   1) Their type could be CastableTypedEnumResult<TypedEnum> if they are
Packit f0b94e
//      the result of a bitwise operation themselves;
Packit f0b94e
//   2) In the non-c++11 legacy path, the type of enum values is also
Packit f0b94e
//      different from TypedEnum.
Packit f0b94e
//
Packit f0b94e
template<typename TypedEnum, char o, typename T1, typename T2, typename T3>
Packit f0b94e
void TestBinOp(const T1& aT1, const T2& aT2, const T3& aT3)
Packit f0b94e
{
Packit f0b94e
  typedef typename mozilla::detail::UnsignedIntegerTypeForEnum<TypedEnum>::Type
Packit f0b94e
          UnsignedIntegerType;
Packit f0b94e
Packit f0b94e
  // Part 1:
Packit f0b94e
  // Test the bitwise binary operator i.e.
Packit f0b94e
  //   result = aT1 Op aT2;
Packit f0b94e
  auto result = Op<o>(aT1, aT2);
Packit f0b94e
Packit f0b94e
  typedef decltype(result) ResultType;
Packit f0b94e
Packit f0b94e
  RequireLiteralType<ResultType>();
Packit f0b94e
  TestNonConvertibilityForOneType<ResultType>();
Packit f0b94e
Packit f0b94e
  UnsignedIntegerType unsignedIntegerResult =
Packit f0b94e
    Op<o>(UnsignedIntegerType(aT1), UnsignedIntegerType(aT2));
Packit f0b94e
Packit f0b94e
  MOZ_RELEASE_ASSERT(unsignedIntegerResult == UnsignedIntegerType(result));
Packit f0b94e
  MOZ_RELEASE_ASSERT(TypedEnum(unsignedIntegerResult) == TypedEnum(result));
Packit f0b94e
  MOZ_RELEASE_ASSERT((!unsignedIntegerResult) == (!result));
Packit f0b94e
  MOZ_RELEASE_ASSERT((!!unsignedIntegerResult) == (!!result));
Packit f0b94e
  MOZ_RELEASE_ASSERT(bool(unsignedIntegerResult) == bool(result));
Packit f0b94e
Packit f0b94e
  // Part 2:
Packit f0b94e
  // Test the compound-assignment operator, i.e.
Packit f0b94e
  //   result Op= aT3;
Packit f0b94e
  TypedEnum newResult = result;
Packit f0b94e
  OpAssign<o>(newResult, aT3);
Packit f0b94e
  UnsignedIntegerType unsignedIntegerNewResult = unsignedIntegerResult;
Packit f0b94e
  OpAssign<o>(unsignedIntegerNewResult, UnsignedIntegerType(aT3));
Packit f0b94e
  MOZ_RELEASE_ASSERT(TypedEnum(unsignedIntegerNewResult) == newResult);
Packit f0b94e
Packit f0b94e
  // Part 3:
Packit f0b94e
  // Test additional boolean operators that we unfortunately had to add to
Packit f0b94e
  // CastableTypedEnumResult at some point to please some compiler,
Packit f0b94e
  // even though bool convertibility should have been enough.
Packit f0b94e
  MOZ_RELEASE_ASSERT(result == TypedEnum(result));
Packit f0b94e
  MOZ_RELEASE_ASSERT(!(result != TypedEnum(result)));
Packit f0b94e
  MOZ_RELEASE_ASSERT((result && true) == bool(result));
Packit f0b94e
  MOZ_RELEASE_ASSERT((result && false) == false);
Packit f0b94e
  MOZ_RELEASE_ASSERT((true && result) == bool(result));
Packit f0b94e
  MOZ_RELEASE_ASSERT((false && result && false) == false);
Packit f0b94e
  MOZ_RELEASE_ASSERT((result || false) == bool(result));
Packit f0b94e
  MOZ_RELEASE_ASSERT((result || true) == true);
Packit f0b94e
  MOZ_RELEASE_ASSERT((false || result) == bool(result));
Packit f0b94e
  MOZ_RELEASE_ASSERT((true || result) == true);
Packit f0b94e
Packit f0b94e
  // Part 4:
Packit f0b94e
  // Test short-circuit evaluation.
Packit f0b94e
  auto Explode = [] {
Packit f0b94e
    // This function should never be called. Return an arbitrary value.
Packit f0b94e
    MOZ_RELEASE_ASSERT(false);
Packit f0b94e
    return false;
Packit f0b94e
  };
Packit f0b94e
  if (result) {
Packit f0b94e
    MOZ_RELEASE_ASSERT(result || Explode());
Packit f0b94e
    MOZ_RELEASE_ASSERT(!(!result && Explode()));
Packit f0b94e
  } else {
Packit f0b94e
    MOZ_RELEASE_ASSERT(!(result && Explode()));
Packit f0b94e
    MOZ_RELEASE_ASSERT(!result || Explode());
Packit f0b94e
  }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
// Similar to TestBinOp but testing the unary ~ operator.
Packit f0b94e
template<typename TypedEnum, typename T>
Packit f0b94e
void TestTilde(const T& aT)
Packit f0b94e
{
Packit f0b94e
  typedef typename mozilla::detail::UnsignedIntegerTypeForEnum<TypedEnum>::Type
Packit f0b94e
          UnsignedIntegerType;
Packit f0b94e
Packit f0b94e
  auto result = ~aT;
Packit f0b94e
Packit f0b94e
  typedef decltype(result) ResultType;
Packit f0b94e
Packit f0b94e
  RequireLiteralType<ResultType>();
Packit f0b94e
  TestNonConvertibilityForOneType<ResultType>();
Packit f0b94e
Packit f0b94e
  UnsignedIntegerType unsignedIntegerResult = ~(UnsignedIntegerType(aT));
Packit f0b94e
Packit f0b94e
  MOZ_RELEASE_ASSERT(unsignedIntegerResult == UnsignedIntegerType(result));
Packit f0b94e
  MOZ_RELEASE_ASSERT(TypedEnum(unsignedIntegerResult) == TypedEnum(result));
Packit f0b94e
  MOZ_RELEASE_ASSERT((!unsignedIntegerResult) == (!result));
Packit f0b94e
  MOZ_RELEASE_ASSERT((!!unsignedIntegerResult) == (!!result));
Packit f0b94e
  MOZ_RELEASE_ASSERT(bool(unsignedIntegerResult) == bool(result));
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
// Helper dispatching a given triple of operands to all operator-specific
Packit f0b94e
// testing functions.
Packit f0b94e
template<typename TypedEnum, typename T1, typename T2, typename T3>
Packit f0b94e
void TestAllOpsForGivenOperands(const T1& aT1, const T2& aT2, const T3& aT3)
Packit f0b94e
{
Packit f0b94e
  TestBinOp<TypedEnum, '|'>(aT1, aT2, aT3);
Packit f0b94e
  TestBinOp<TypedEnum, '&'>(aT1, aT2, aT3);
Packit f0b94e
  TestBinOp<TypedEnum, '^'>(aT1, aT2, aT3);
Packit f0b94e
  TestTilde<TypedEnum>(aT1);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
// Helper building various triples of operands using a given operator,
Packit f0b94e
// and testing all operators with them.
Packit f0b94e
template<typename TypedEnum, char o>
Packit f0b94e
void TestAllOpsForOperandsBuiltUsingGivenOp()
Packit f0b94e
{
Packit f0b94e
  // The type of enum values like TypedEnum::A may be different from
Packit f0b94e
  // TypedEnum. That is the case in the legacy non-C++11 path. We want to
Packit f0b94e
  // ensure good test coverage even when these two types are distinct.
Packit f0b94e
  // To that effect, we have both 'auto' typed variables, preserving the
Packit f0b94e
  // original type of enum values, and 'plain' typed variables, that
Packit f0b94e
  // are plain TypedEnum's.
Packit f0b94e
Packit f0b94e
  const TypedEnum a_plain = TypedEnum::A;
Packit f0b94e
  const TypedEnum b_plain = TypedEnum::B;
Packit f0b94e
  const TypedEnum c_plain = TypedEnum::C;
Packit f0b94e
Packit f0b94e
  auto a_auto = TypedEnum::A;
Packit f0b94e
  auto b_auto = TypedEnum::B;
Packit f0b94e
  auto c_auto = TypedEnum::C;
Packit f0b94e
Packit f0b94e
  auto ab_plain = Op<o>(a_plain, b_plain);
Packit f0b94e
  auto bc_plain = Op<o>(b_plain, c_plain);
Packit f0b94e
  auto ab_auto = Op<o>(a_auto, b_auto);
Packit f0b94e
  auto bc_auto = Op<o>(b_auto, c_auto);
Packit f0b94e
Packit f0b94e
  // On each row below, we pass a triple of operands. Keep in mind that this
Packit f0b94e
  // is going to be received as (aT1, aT2, aT3) and the actual tests performed
Packit f0b94e
  // will be of the form
Packit f0b94e
  //
Packit f0b94e
  //   result = aT1 Op aT2;
Packit f0b94e
  //   result Op= aT3;
Packit f0b94e
  //
Packit f0b94e
  // For this reason, we carefully ensure that the values of (aT1, aT2)
Packit f0b94e
  // systematically cover all types of such pairs; to limit complexity,
Packit f0b94e
  // we are not so careful with aT3, and we just try to pass aT3's
Packit f0b94e
  // that may lead to nontrivial bitwise operations.
Packit f0b94e
  TestAllOpsForGivenOperands<TypedEnum>(a_plain,  b_plain,  c_plain);
Packit f0b94e
  TestAllOpsForGivenOperands<TypedEnum>(a_plain,  bc_plain, b_auto);
Packit f0b94e
  TestAllOpsForGivenOperands<TypedEnum>(ab_plain, c_plain,  a_plain);
Packit f0b94e
  TestAllOpsForGivenOperands<TypedEnum>(ab_plain, bc_plain, a_auto);
Packit f0b94e
Packit f0b94e
  TestAllOpsForGivenOperands<TypedEnum>(a_plain,  b_auto,   c_plain);
Packit f0b94e
  TestAllOpsForGivenOperands<TypedEnum>(a_plain,  bc_auto,  b_auto);
Packit f0b94e
  TestAllOpsForGivenOperands<TypedEnum>(ab_plain, c_auto,   a_plain);
Packit f0b94e
  TestAllOpsForGivenOperands<TypedEnum>(ab_plain, bc_auto,  a_auto);
Packit f0b94e
Packit f0b94e
  TestAllOpsForGivenOperands<TypedEnum>(a_auto,   b_plain,  c_plain);
Packit f0b94e
  TestAllOpsForGivenOperands<TypedEnum>(a_auto,   bc_plain, b_auto);
Packit f0b94e
  TestAllOpsForGivenOperands<TypedEnum>(ab_auto,  c_plain,  a_plain);
Packit f0b94e
  TestAllOpsForGivenOperands<TypedEnum>(ab_auto,  bc_plain, a_auto);
Packit f0b94e
Packit f0b94e
  TestAllOpsForGivenOperands<TypedEnum>(a_auto,   b_auto,   c_plain);
Packit f0b94e
  TestAllOpsForGivenOperands<TypedEnum>(a_auto,   bc_auto,  b_auto);
Packit f0b94e
  TestAllOpsForGivenOperands<TypedEnum>(ab_auto,  c_auto,   a_plain);
Packit f0b94e
  TestAllOpsForGivenOperands<TypedEnum>(ab_auto,  bc_auto,  a_auto);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
// Tests all bitwise operations on a given TypedEnum bitfield.
Packit f0b94e
template<typename TypedEnum>
Packit f0b94e
void
Packit f0b94e
TestTypedEnumBitField()
Packit f0b94e
{
Packit f0b94e
  TestTypedEnumBasics<TypedEnum>();
Packit f0b94e
Packit f0b94e
  TestAllOpsForOperandsBuiltUsingGivenOp<TypedEnum, '|'>();
Packit f0b94e
  TestAllOpsForOperandsBuiltUsingGivenOp<TypedEnum, '&'>();
Packit f0b94e
  TestAllOpsForOperandsBuiltUsingGivenOp<TypedEnum, '^'>();
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
// Checks that enum bitwise expressions have the same non-convertibility
Packit f0b94e
// properties as c++11 enum classes do, i.e. not implicitly convertible to
Packit f0b94e
// anything (though *explicitly* convertible).
Packit f0b94e
void TestNoConversionsBetweenUnrelatedTypes()
Packit f0b94e
{
Packit f0b94e
  using mozilla::IsConvertible;
Packit f0b94e
Packit f0b94e
  // Two typed enum classes having the same underlying integer type, to ensure
Packit f0b94e
  // that we would catch bugs accidentally allowing conversions in that case.
Packit f0b94e
  typedef CharEnumBitField T1;
Packit f0b94e
  typedef Nested::CharEnumBitField T2;
Packit f0b94e
Packit f0b94e
  static_assert(!IsConvertible<T1, T2>::value,
Packit f0b94e
                "should not be convertible");
Packit f0b94e
  static_assert(!IsConvertible<T1, decltype(T2::A)>::value,
Packit f0b94e
                "should not be convertible");
Packit f0b94e
  static_assert(!IsConvertible<T1, decltype(T2::A | T2::B)>::value,
Packit f0b94e
                "should not be convertible");
Packit f0b94e
Packit f0b94e
  static_assert(!IsConvertible<decltype(T1::A), T2>::value,
Packit f0b94e
                "should not be convertible");
Packit f0b94e
  static_assert(!IsConvertible<decltype(T1::A), decltype(T2::A)>::value,
Packit f0b94e
                "should not be convertible");
Packit f0b94e
  static_assert(!IsConvertible<decltype(T1::A), decltype(T2::A | T2::B)>::value,
Packit f0b94e
                "should not be convertible");
Packit f0b94e
Packit f0b94e
  static_assert(!IsConvertible<decltype(T1::A | T1::B), T2>::value,
Packit f0b94e
                "should not be convertible");
Packit f0b94e
  static_assert(!IsConvertible<decltype(T1::A | T1::B), decltype(T2::A)>::value,
Packit f0b94e
                "should not be convertible");
Packit f0b94e
  static_assert(!IsConvertible<decltype(T1::A | T1::B), decltype(T2::A | T2::B)>::value,
Packit f0b94e
                "should not be convertible");
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
enum class Int8EnumWithHighBits : int8_t {
Packit f0b94e
  A = 0x20,
Packit f0b94e
  B = 0x40
Packit f0b94e
};
Packit f0b94e
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Int8EnumWithHighBits)
Packit f0b94e
Packit f0b94e
enum class Uint8EnumWithHighBits : uint8_t {
Packit f0b94e
  A = 0x40,
Packit f0b94e
  B = 0x80
Packit f0b94e
};
Packit f0b94e
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Uint8EnumWithHighBits)
Packit f0b94e
Packit f0b94e
enum class Int16EnumWithHighBits : int16_t {
Packit f0b94e
  A = 0x2000,
Packit f0b94e
  B = 0x4000
Packit f0b94e
};
Packit f0b94e
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Int16EnumWithHighBits)
Packit f0b94e
Packit f0b94e
enum class Uint16EnumWithHighBits : uint16_t {
Packit f0b94e
  A = 0x4000,
Packit f0b94e
  B = 0x8000
Packit f0b94e
};
Packit f0b94e
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Uint16EnumWithHighBits)
Packit f0b94e
Packit f0b94e
enum class Int32EnumWithHighBits : int32_t {
Packit f0b94e
  A = 0x20000000,
Packit f0b94e
  B = 0x40000000
Packit f0b94e
};
Packit f0b94e
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Int32EnumWithHighBits)
Packit f0b94e
Packit f0b94e
enum class Uint32EnumWithHighBits : uint32_t {
Packit f0b94e
  A = 0x40000000u,
Packit f0b94e
  B = 0x80000000u
Packit f0b94e
};
Packit f0b94e
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Uint32EnumWithHighBits)
Packit f0b94e
Packit f0b94e
enum class Int64EnumWithHighBits : int64_t {
Packit f0b94e
  A = 0x2000000000000000ll,
Packit f0b94e
  B = 0x4000000000000000ll
Packit f0b94e
};
Packit f0b94e
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Int64EnumWithHighBits)
Packit f0b94e
Packit f0b94e
enum class Uint64EnumWithHighBits : uint64_t {
Packit f0b94e
  A = 0x4000000000000000ull,
Packit f0b94e
  B = 0x8000000000000000ull
Packit f0b94e
};
Packit f0b94e
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Uint64EnumWithHighBits)
Packit f0b94e
Packit f0b94e
// Checks that we don't accidentally truncate high bits by coercing to the wrong
Packit f0b94e
// integer type internally when implementing bitwise ops.
Packit f0b94e
template<typename EnumType, typename IntType>
Packit f0b94e
void TestIsNotTruncated()
Packit f0b94e
{
Packit f0b94e
  EnumType a = EnumType::A;
Packit f0b94e
  EnumType b = EnumType::B;
Packit f0b94e
  MOZ_RELEASE_ASSERT(IntType(a));
Packit f0b94e
  MOZ_RELEASE_ASSERT(IntType(b));
Packit f0b94e
  MOZ_RELEASE_ASSERT(a | EnumType::B);
Packit f0b94e
  MOZ_RELEASE_ASSERT(a | b);
Packit f0b94e
  MOZ_RELEASE_ASSERT(EnumType::A | EnumType::B);
Packit f0b94e
  EnumType c = EnumType::A | EnumType::B;
Packit f0b94e
  MOZ_RELEASE_ASSERT(IntType(c));
Packit f0b94e
  MOZ_RELEASE_ASSERT(c & c);
Packit f0b94e
  MOZ_RELEASE_ASSERT(c | c);
Packit f0b94e
  MOZ_RELEASE_ASSERT(c == (EnumType::A | EnumType::B));
Packit f0b94e
  MOZ_RELEASE_ASSERT(a != (EnumType::A | EnumType::B));
Packit f0b94e
  MOZ_RELEASE_ASSERT(b != (EnumType::A | EnumType::B));
Packit f0b94e
  MOZ_RELEASE_ASSERT(c & EnumType::A);
Packit f0b94e
  MOZ_RELEASE_ASSERT(c & EnumType::B);
Packit f0b94e
  EnumType d = EnumType::A;
Packit f0b94e
  d |= EnumType::B;
Packit f0b94e
  MOZ_RELEASE_ASSERT(d == c);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
int
Packit f0b94e
main()
Packit f0b94e
{
Packit f0b94e
  TestTypedEnumBasics<AutoEnum>();
Packit f0b94e
  TestTypedEnumBasics<CharEnum>();
Packit f0b94e
  TestTypedEnumBasics<Nested::AutoEnum>();
Packit f0b94e
  TestTypedEnumBasics<Nested::CharEnum>();
Packit f0b94e
Packit f0b94e
  TestTypedEnumBitField<AutoEnumBitField>();
Packit f0b94e
  TestTypedEnumBitField<CharEnumBitField>();
Packit f0b94e
  TestTypedEnumBitField<Nested::AutoEnumBitField>();
Packit f0b94e
  TestTypedEnumBitField<Nested::CharEnumBitField>();
Packit f0b94e
Packit f0b94e
  TestTypedEnumBitField<BitFieldFor_uint8_t>();
Packit f0b94e
  TestTypedEnumBitField<BitFieldFor_int8_t>();
Packit f0b94e
  TestTypedEnumBitField<BitFieldFor_uint16_t>();
Packit f0b94e
  TestTypedEnumBitField<BitFieldFor_int16_t>();
Packit f0b94e
  TestTypedEnumBitField<BitFieldFor_uint32_t>();
Packit f0b94e
  TestTypedEnumBitField<BitFieldFor_int32_t>();
Packit f0b94e
  TestTypedEnumBitField<BitFieldFor_uint64_t>();
Packit f0b94e
  TestTypedEnumBitField<BitFieldFor_int64_t>();
Packit f0b94e
  TestTypedEnumBitField<BitFieldFor_char>();
Packit f0b94e
  TestTypedEnumBitField<BitFieldFor_signed_char>();
Packit f0b94e
  TestTypedEnumBitField<BitFieldFor_unsigned_char>();
Packit f0b94e
  TestTypedEnumBitField<BitFieldFor_short>();
Packit f0b94e
  TestTypedEnumBitField<BitFieldFor_unsigned_short>();
Packit f0b94e
  TestTypedEnumBitField<BitFieldFor_int>();
Packit f0b94e
  TestTypedEnumBitField<BitFieldFor_unsigned_int>();
Packit f0b94e
  TestTypedEnumBitField<BitFieldFor_long>();
Packit f0b94e
  TestTypedEnumBitField<BitFieldFor_unsigned_long>();
Packit f0b94e
  TestTypedEnumBitField<BitFieldFor_long_long>();
Packit f0b94e
  TestTypedEnumBitField<BitFieldFor_unsigned_long_long>();
Packit f0b94e
Packit f0b94e
  TestNoConversionsBetweenUnrelatedTypes();
Packit f0b94e
Packit f0b94e
  TestIsNotTruncated<Int8EnumWithHighBits, int8_t>();
Packit f0b94e
  TestIsNotTruncated<Int16EnumWithHighBits, int16_t>();
Packit f0b94e
  TestIsNotTruncated<Int32EnumWithHighBits, int32_t>();
Packit f0b94e
  TestIsNotTruncated<Int64EnumWithHighBits, int64_t>();
Packit f0b94e
  TestIsNotTruncated<Uint8EnumWithHighBits, uint8_t>();
Packit f0b94e
  TestIsNotTruncated<Uint16EnumWithHighBits, uint16_t>();
Packit f0b94e
  TestIsNotTruncated<Uint32EnumWithHighBits, uint32_t>();
Packit f0b94e
  TestIsNotTruncated<Uint64EnumWithHighBits, uint64_t>();
Packit f0b94e
Packit f0b94e
  return 0;
Packit f0b94e
}