mfbt/tests/TestTypedEnum.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 #include "mozilla/Assertions.h"
michael@0 6 #include "mozilla/Attributes.h"
michael@0 7 #include "mozilla/TypedEnum.h"
michael@0 8 #include "mozilla/TypedEnumBits.h"
michael@0 9
michael@0 10 #include <stdint.h>
michael@0 11
michael@0 12 // A rough feature check for is_literal_type. Not very carefully checked.
michael@0 13 // Feel free to amend as needed.
michael@0 14 // We leave ANDROID out because it's using stlport which doesn't have std::is_literal_type.
michael@0 15 #if __cplusplus >= 201103L && !defined(ANDROID)
michael@0 16 # if defined(__clang__)
michael@0 17 /*
michael@0 18 * Per Clang documentation, "Note that marketing version numbers should not
michael@0 19 * be used to check for language features, as different vendors use different
michael@0 20 * numbering schemes. Instead, use the feature checking macros."
michael@0 21 */
michael@0 22 # ifndef __has_extension
michael@0 23 # define __has_extension __has_feature /* compatibility, for older versions of clang */
michael@0 24 # endif
michael@0 25 # if __has_extension(is_literal) && __has_include(<type_traits>)
michael@0 26 # define MOZ_HAVE_IS_LITERAL
michael@0 27 # endif
michael@0 28 # elif defined(__GNUC__)
michael@0 29 # if defined(__GXX_EXPERIMENTAL_CXX0X__)
michael@0 30 # if MOZ_GCC_VERSION_AT_LEAST(4, 6, 0)
michael@0 31 # define MOZ_HAVE_IS_LITERAL
michael@0 32 # endif
michael@0 33 # endif
michael@0 34 # elif defined(_MSC_VER)
michael@0 35 # if _MSC_VER >= 1700
michael@0 36 # define MOZ_HAVE_IS_LITERAL
michael@0 37 # endif
michael@0 38 # endif
michael@0 39 #endif
michael@0 40
michael@0 41 #ifdef MOZ_HAVE_IS_LITERAL
michael@0 42 #include <type_traits>
michael@0 43 template<typename T>
michael@0 44 void
michael@0 45 RequireLiteralType()
michael@0 46 {
michael@0 47 static_assert(std::is_literal_type<T>::value, "Expected a literal type");
michael@0 48 }
michael@0 49 #else // not MOZ_HAVE_IS_LITERAL
michael@0 50 template<typename T>
michael@0 51 void
michael@0 52 RequireLiteralType()
michael@0 53 {
michael@0 54 }
michael@0 55 #endif
michael@0 56
michael@0 57 template<typename T>
michael@0 58 void
michael@0 59 RequireLiteralType(const T&)
michael@0 60 {
michael@0 61 RequireLiteralType<T>();
michael@0 62 }
michael@0 63
michael@0 64 MOZ_BEGIN_ENUM_CLASS(AutoEnum)
michael@0 65 A,
michael@0 66 B = -3,
michael@0 67 C
michael@0 68 MOZ_END_ENUM_CLASS(AutoEnum)
michael@0 69
michael@0 70 MOZ_BEGIN_ENUM_CLASS(CharEnum, char)
michael@0 71 A,
michael@0 72 B = 3,
michael@0 73 C
michael@0 74 MOZ_END_ENUM_CLASS(CharEnum)
michael@0 75
michael@0 76 MOZ_BEGIN_ENUM_CLASS(AutoEnumBitField)
michael@0 77 A = 0x10,
michael@0 78 B = 0x20,
michael@0 79 C
michael@0 80 MOZ_END_ENUM_CLASS(AutoEnumBitField)
michael@0 81
michael@0 82 MOZ_BEGIN_ENUM_CLASS(CharEnumBitField, char)
michael@0 83 A = 0x10,
michael@0 84 B,
michael@0 85 C = 0x40
michael@0 86 MOZ_END_ENUM_CLASS(CharEnumBitField)
michael@0 87
michael@0 88 struct Nested
michael@0 89 {
michael@0 90 MOZ_BEGIN_NESTED_ENUM_CLASS(AutoEnum)
michael@0 91 A,
michael@0 92 B,
michael@0 93 C = -1
michael@0 94 MOZ_END_NESTED_ENUM_CLASS(AutoEnum)
michael@0 95
michael@0 96 MOZ_BEGIN_NESTED_ENUM_CLASS(CharEnum, char)
michael@0 97 A = 4,
michael@0 98 B,
michael@0 99 C = 1
michael@0 100 MOZ_END_NESTED_ENUM_CLASS(CharEnum)
michael@0 101
michael@0 102 MOZ_BEGIN_NESTED_ENUM_CLASS(AutoEnumBitField)
michael@0 103 A,
michael@0 104 B = 0x20,
michael@0 105 C
michael@0 106 MOZ_END_NESTED_ENUM_CLASS(AutoEnumBitField)
michael@0 107
michael@0 108 MOZ_BEGIN_NESTED_ENUM_CLASS(CharEnumBitField, char)
michael@0 109 A = 1,
michael@0 110 B = 1,
michael@0 111 C = 1
michael@0 112 MOZ_END_NESTED_ENUM_CLASS(CharEnumBitField)
michael@0 113 };
michael@0 114
michael@0 115 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(AutoEnumBitField)
michael@0 116 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(CharEnumBitField)
michael@0 117 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Nested::AutoEnumBitField)
michael@0 118 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Nested::CharEnumBitField)
michael@0 119
michael@0 120 #define MAKE_STANDARD_BITFIELD_FOR_TYPE(IntType) \
michael@0 121 MOZ_BEGIN_ENUM_CLASS(BitFieldFor_##IntType, IntType) \
michael@0 122 A = 1, \
michael@0 123 B = 2, \
michael@0 124 C = 4, \
michael@0 125 MOZ_END_ENUM_CLASS(BitFieldFor_##IntType) \
michael@0 126 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(BitFieldFor_##IntType)
michael@0 127
michael@0 128 MAKE_STANDARD_BITFIELD_FOR_TYPE(int8_t)
michael@0 129 MAKE_STANDARD_BITFIELD_FOR_TYPE(uint8_t)
michael@0 130 MAKE_STANDARD_BITFIELD_FOR_TYPE(int16_t)
michael@0 131 MAKE_STANDARD_BITFIELD_FOR_TYPE(uint16_t)
michael@0 132 MAKE_STANDARD_BITFIELD_FOR_TYPE(int32_t)
michael@0 133 MAKE_STANDARD_BITFIELD_FOR_TYPE(uint32_t)
michael@0 134 MAKE_STANDARD_BITFIELD_FOR_TYPE(int64_t)
michael@0 135 MAKE_STANDARD_BITFIELD_FOR_TYPE(uint64_t)
michael@0 136 MAKE_STANDARD_BITFIELD_FOR_TYPE(char)
michael@0 137 typedef signed char signed_char;
michael@0 138 MAKE_STANDARD_BITFIELD_FOR_TYPE(signed_char)
michael@0 139 typedef unsigned char unsigned_char;
michael@0 140 MAKE_STANDARD_BITFIELD_FOR_TYPE(unsigned_char)
michael@0 141 MAKE_STANDARD_BITFIELD_FOR_TYPE(short)
michael@0 142 typedef unsigned short unsigned_short;
michael@0 143 MAKE_STANDARD_BITFIELD_FOR_TYPE(unsigned_short)
michael@0 144 MAKE_STANDARD_BITFIELD_FOR_TYPE(int)
michael@0 145 typedef unsigned int unsigned_int;
michael@0 146 MAKE_STANDARD_BITFIELD_FOR_TYPE(unsigned_int)
michael@0 147 MAKE_STANDARD_BITFIELD_FOR_TYPE(long)
michael@0 148 typedef unsigned long unsigned_long;
michael@0 149 MAKE_STANDARD_BITFIELD_FOR_TYPE(unsigned_long)
michael@0 150 typedef long long long_long;
michael@0 151 MAKE_STANDARD_BITFIELD_FOR_TYPE(long_long)
michael@0 152 typedef unsigned long long unsigned_long_long;
michael@0 153 MAKE_STANDARD_BITFIELD_FOR_TYPE(unsigned_long_long)
michael@0 154
michael@0 155 #undef MAKE_STANDARD_BITFIELD_FOR_TYPE
michael@0 156
michael@0 157 template<typename T>
michael@0 158 void
michael@0 159 TestNonConvertibilityForOneType()
michael@0 160 {
michael@0 161 using mozilla::IsConvertible;
michael@0 162
michael@0 163 #if defined(MOZ_HAVE_CXX11_STRONG_ENUMS) && defined(MOZ_HAVE_EXPLICIT_CONVERSION)
michael@0 164 static_assert(!IsConvertible<T, bool>::value, "should not be convertible");
michael@0 165 static_assert(!IsConvertible<T, int>::value, "should not be convertible");
michael@0 166 static_assert(!IsConvertible<T, uint64_t>::value, "should not be convertible");
michael@0 167 #endif
michael@0 168
michael@0 169 static_assert(!IsConvertible<bool, T>::value, "should not be convertible");
michael@0 170 static_assert(!IsConvertible<int, T>::value, "should not be convertible");
michael@0 171 static_assert(!IsConvertible<uint64_t, T>::value, "should not be convertible");
michael@0 172 }
michael@0 173
michael@0 174 template<typename TypedEnum>
michael@0 175 void
michael@0 176 TestTypedEnumBasics()
michael@0 177 {
michael@0 178 const TypedEnum a = TypedEnum::A;
michael@0 179 int unused = int(a);
michael@0 180 (void) unused;
michael@0 181 RequireLiteralType(TypedEnum::A);
michael@0 182 RequireLiteralType(a);
michael@0 183 TestNonConvertibilityForOneType<TypedEnum>();
michael@0 184 }
michael@0 185
michael@0 186 // Op wraps a bitwise binary operator, passed as a char template parameter,
michael@0 187 // and applies it to its arguments (t1, t2). For example,
michael@0 188 //
michael@0 189 // Op<'|'>(t1, t2)
michael@0 190 //
michael@0 191 // is the same as
michael@0 192 //
michael@0 193 // t1 | t2.
michael@0 194 //
michael@0 195 template<char o, typename T1, typename T2>
michael@0 196 auto Op(const T1& t1, const T2& t2)
michael@0 197 -> decltype(t1 | t2) // See the static_assert's below --- the return type
michael@0 198 // depends solely on the operands type, not on the
michael@0 199 // choice of operation.
michael@0 200 {
michael@0 201 using mozilla::IsSame;
michael@0 202 static_assert(IsSame<decltype(t1 | t2), decltype(t1 & t2)>::value,
michael@0 203 "binary ops should have the same result type");
michael@0 204 static_assert(IsSame<decltype(t1 | t2), decltype(t1 ^ t2)>::value,
michael@0 205 "binary ops should have the same result type");
michael@0 206
michael@0 207 static_assert(o == '|' ||
michael@0 208 o == '&' ||
michael@0 209 o == '^', "unexpected operator character");
michael@0 210
michael@0 211 return o == '|' ? t1 | t2
michael@0 212 : o == '&' ? t1 & t2
michael@0 213 : t1 ^ t2;
michael@0 214 }
michael@0 215
michael@0 216 // OpAssign wraps a bitwise binary operator, passed as a char template
michael@0 217 // parameter, and applies the corresponding compound-assignment operator to its
michael@0 218 // arguments (t1, t2). For example,
michael@0 219 //
michael@0 220 // OpAssign<'|'>(t1, t2)
michael@0 221 //
michael@0 222 // is the same as
michael@0 223 //
michael@0 224 // t1 |= t2.
michael@0 225 //
michael@0 226 template<char o, typename T1, typename T2>
michael@0 227 T1& OpAssign(T1& t1, const T2& t2)
michael@0 228 {
michael@0 229 static_assert(o == '|' ||
michael@0 230 o == '&' ||
michael@0 231 o == '^', "unexpected operator character");
michael@0 232
michael@0 233 switch (o) {
michael@0 234 case '|': return t1 |= t2;
michael@0 235 case '&': return t1 &= t2;
michael@0 236 case '^': return t1 ^= t2;
michael@0 237 default: MOZ_CRASH();
michael@0 238 }
michael@0 239 }
michael@0 240
michael@0 241 // Tests a single binary bitwise operator, using a single set of three operands.
michael@0 242 // The operations tested are:
michael@0 243 //
michael@0 244 // result = t1 Op t2;
michael@0 245 // result Op= t3;
michael@0 246 //
michael@0 247 // Where Op is the operator specified by the char template parameter 'o' and can
michael@0 248 // be any of '|', '&', '^'.
michael@0 249 //
michael@0 250 // Note that the operands t1, t2, t3 are intentionally passed with free types
michael@0 251 // (separate template parameters for each) because their type may actually be
michael@0 252 // different from TypedEnum:
michael@0 253 // 1) Their type could be CastableTypedEnumResult<TypedEnum> if they are
michael@0 254 // the result of a bitwise operation themselves;
michael@0 255 // 2) In the non-c++11 legacy path, the type of enum values is also
michael@0 256 // different from TypedEnum.
michael@0 257 //
michael@0 258 template<typename TypedEnum, char o, typename T1, typename T2, typename T3>
michael@0 259 void TestBinOp(const T1& t1, const T2& t2, const T3& t3)
michael@0 260 {
michael@0 261 typedef typename mozilla::detail::UnsignedIntegerTypeForEnum<TypedEnum>::Type
michael@0 262 UnsignedIntegerType;
michael@0 263
michael@0 264 // Part 1:
michael@0 265 // Test the bitwise binary operator i.e.
michael@0 266 // result = t1 Op t2;
michael@0 267 auto result = Op<o>(t1, t2);
michael@0 268
michael@0 269 typedef decltype(result) ResultType;
michael@0 270
michael@0 271 RequireLiteralType<ResultType>();
michael@0 272 TestNonConvertibilityForOneType<ResultType>();
michael@0 273
michael@0 274 UnsignedIntegerType unsignedIntegerResult
michael@0 275 = Op<o>(UnsignedIntegerType(t1), UnsignedIntegerType(t2));
michael@0 276
michael@0 277 MOZ_RELEASE_ASSERT(unsignedIntegerResult == UnsignedIntegerType(result));
michael@0 278 MOZ_RELEASE_ASSERT(TypedEnum(unsignedIntegerResult) == TypedEnum(result));
michael@0 279 MOZ_RELEASE_ASSERT((!unsignedIntegerResult) == (!result));
michael@0 280 MOZ_RELEASE_ASSERT((!!unsignedIntegerResult) == (!!result));
michael@0 281 MOZ_RELEASE_ASSERT(bool(unsignedIntegerResult) == bool(result));
michael@0 282
michael@0 283 // Part 2:
michael@0 284 // Test the compound-assignment operator, i.e.
michael@0 285 // result Op= t3;
michael@0 286 TypedEnum newResult = result;
michael@0 287 OpAssign<o>(newResult, t3);
michael@0 288 UnsignedIntegerType unsignedIntegerNewResult = unsignedIntegerResult;
michael@0 289 OpAssign<o>(unsignedIntegerNewResult, UnsignedIntegerType(t3));
michael@0 290 MOZ_RELEASE_ASSERT(TypedEnum(unsignedIntegerNewResult) == newResult);
michael@0 291
michael@0 292 // Part 3:
michael@0 293 // Test additional boolean operators that we unfortunately had to add to
michael@0 294 // CastableTypedEnumResult at some point to please some compiler,
michael@0 295 // even though bool convertibility should have been enough.
michael@0 296 MOZ_RELEASE_ASSERT(result == TypedEnum(result));
michael@0 297 MOZ_RELEASE_ASSERT(!(result != TypedEnum(result)));
michael@0 298 MOZ_RELEASE_ASSERT((result && true) == bool(result));
michael@0 299 MOZ_RELEASE_ASSERT((result && false) == false);
michael@0 300 MOZ_RELEASE_ASSERT((true && result) == bool(result));
michael@0 301 MOZ_RELEASE_ASSERT((false && result && false) == false);
michael@0 302 MOZ_RELEASE_ASSERT((result || false) == bool(result));
michael@0 303 MOZ_RELEASE_ASSERT((result || true) == true);
michael@0 304 MOZ_RELEASE_ASSERT((false || result) == bool(result));
michael@0 305 MOZ_RELEASE_ASSERT((true || result) == true);
michael@0 306 }
michael@0 307
michael@0 308 // Similar to TestBinOp but testing the unary ~ operator.
michael@0 309 template<typename TypedEnum, typename T>
michael@0 310 void TestTilde(const T& t)
michael@0 311 {
michael@0 312 typedef typename mozilla::detail::UnsignedIntegerTypeForEnum<TypedEnum>::Type
michael@0 313 UnsignedIntegerType;
michael@0 314
michael@0 315 auto result = ~t;
michael@0 316
michael@0 317 typedef decltype(result) ResultType;
michael@0 318
michael@0 319 RequireLiteralType<ResultType>();
michael@0 320 TestNonConvertibilityForOneType<ResultType>();
michael@0 321
michael@0 322 UnsignedIntegerType unsignedIntegerResult = ~(UnsignedIntegerType(t));
michael@0 323
michael@0 324 MOZ_RELEASE_ASSERT(unsignedIntegerResult == UnsignedIntegerType(result));
michael@0 325 MOZ_RELEASE_ASSERT(TypedEnum(unsignedIntegerResult) == TypedEnum(result));
michael@0 326 MOZ_RELEASE_ASSERT((!unsignedIntegerResult) == (!result));
michael@0 327 MOZ_RELEASE_ASSERT((!!unsignedIntegerResult) == (!!result));
michael@0 328 MOZ_RELEASE_ASSERT(bool(unsignedIntegerResult) == bool(result));
michael@0 329 }
michael@0 330
michael@0 331 // Helper dispatching a given triple of operands to all operator-specific
michael@0 332 // testing functions.
michael@0 333 template<typename TypedEnum, typename T1, typename T2, typename T3>
michael@0 334 void TestAllOpsForGivenOperands(const T1& t1, const T2& t2, const T3& t3)
michael@0 335 {
michael@0 336 TestBinOp<TypedEnum, '|'>(t1, t2, t3);
michael@0 337 TestBinOp<TypedEnum, '&'>(t1, t2, t3);
michael@0 338 TestBinOp<TypedEnum, '^'>(t1, t2, t3);
michael@0 339 TestTilde<TypedEnum>(t1);
michael@0 340 }
michael@0 341
michael@0 342 // Helper building various triples of operands using a given operator,
michael@0 343 // and testing all operators with them.
michael@0 344 template<typename TypedEnum, char o>
michael@0 345 void TestAllOpsForOperandsBuiltUsingGivenOp()
michael@0 346 {
michael@0 347 // The type of enum values like TypedEnum::A may be different from
michael@0 348 // TypedEnum. That is the case in the legacy non-C++11 path. We want to
michael@0 349 // ensure good test coverage even when these two types are distinct.
michael@0 350 // To that effect, we have both 'auto' typed variables, preserving the
michael@0 351 // original type of enum values, and 'plain' typed variables, that
michael@0 352 // are plain TypedEnum's.
michael@0 353
michael@0 354 const TypedEnum a_plain = TypedEnum::A;
michael@0 355 const TypedEnum b_plain = TypedEnum::B;
michael@0 356 const TypedEnum c_plain = TypedEnum::C;
michael@0 357
michael@0 358 auto a_auto = TypedEnum::A;
michael@0 359 auto b_auto = TypedEnum::B;
michael@0 360 auto c_auto = TypedEnum::C;
michael@0 361
michael@0 362 auto ab_plain = Op<o>(a_plain, b_plain);
michael@0 363 auto bc_plain = Op<o>(b_plain, c_plain);
michael@0 364 auto ab_auto = Op<o>(a_auto, b_auto);
michael@0 365 auto bc_auto = Op<o>(b_auto, c_auto);
michael@0 366
michael@0 367 // On each row below, we pass a triple of operands. Keep in mind that this
michael@0 368 // is going to be received as (t1, t2, t3) and the actual tests performed
michael@0 369 // will be of the form
michael@0 370 //
michael@0 371 // result = t1 Op t2;
michael@0 372 // result Op= t3;
michael@0 373 //
michael@0 374 // For this reason, we carefully ensure that the values of (t1, t2)
michael@0 375 // systematically cover all types of such pairs; to limit complexity,
michael@0 376 // we are not so careful with t3, and we just try to pass t3's
michael@0 377 // that may lead to nontrivial bitwise operations.
michael@0 378 TestAllOpsForGivenOperands<TypedEnum>(a_plain, b_plain, c_plain);
michael@0 379 TestAllOpsForGivenOperands<TypedEnum>(a_plain, bc_plain, b_auto);
michael@0 380 TestAllOpsForGivenOperands<TypedEnum>(ab_plain, c_plain, a_plain);
michael@0 381 TestAllOpsForGivenOperands<TypedEnum>(ab_plain, bc_plain, a_auto);
michael@0 382
michael@0 383 TestAllOpsForGivenOperands<TypedEnum>(a_plain, b_auto, c_plain);
michael@0 384 TestAllOpsForGivenOperands<TypedEnum>(a_plain, bc_auto, b_auto);
michael@0 385 TestAllOpsForGivenOperands<TypedEnum>(ab_plain, c_auto, a_plain);
michael@0 386 TestAllOpsForGivenOperands<TypedEnum>(ab_plain, bc_auto, a_auto);
michael@0 387
michael@0 388 TestAllOpsForGivenOperands<TypedEnum>(a_auto, b_plain, c_plain);
michael@0 389 TestAllOpsForGivenOperands<TypedEnum>(a_auto, bc_plain, b_auto);
michael@0 390 TestAllOpsForGivenOperands<TypedEnum>(ab_auto, c_plain, a_plain);
michael@0 391 TestAllOpsForGivenOperands<TypedEnum>(ab_auto, bc_plain, a_auto);
michael@0 392
michael@0 393 TestAllOpsForGivenOperands<TypedEnum>(a_auto, b_auto, c_plain);
michael@0 394 TestAllOpsForGivenOperands<TypedEnum>(a_auto, bc_auto, b_auto);
michael@0 395 TestAllOpsForGivenOperands<TypedEnum>(ab_auto, c_auto, a_plain);
michael@0 396 TestAllOpsForGivenOperands<TypedEnum>(ab_auto, bc_auto, a_auto);
michael@0 397 }
michael@0 398
michael@0 399 // Tests all bitwise operations on a given TypedEnum bitfield.
michael@0 400 template<typename TypedEnum>
michael@0 401 void
michael@0 402 TestTypedEnumBitField()
michael@0 403 {
michael@0 404 TestTypedEnumBasics<TypedEnum>();
michael@0 405
michael@0 406 TestAllOpsForOperandsBuiltUsingGivenOp<TypedEnum, '|'>();
michael@0 407 TestAllOpsForOperandsBuiltUsingGivenOp<TypedEnum, '&'>();
michael@0 408 TestAllOpsForOperandsBuiltUsingGivenOp<TypedEnum, '^'>();
michael@0 409 }
michael@0 410
michael@0 411 // Checks that enum bitwise expressions have the same non-convertibility properties as
michael@0 412 // c++11 enum classes do, i.e. not implicitly convertible to anything
michael@0 413 // (though *explicitly* convertible).
michael@0 414 void TestNoConversionsBetweenUnrelatedTypes()
michael@0 415 {
michael@0 416 using mozilla::IsConvertible;
michael@0 417
michael@0 418 // Two typed enum classes having the same underlying integer type, to ensure that
michael@0 419 // we would catch bugs accidentally allowing conversions in that case.
michael@0 420 typedef CharEnumBitField T1;
michael@0 421 typedef Nested::CharEnumBitField T2;
michael@0 422
michael@0 423 static_assert(!IsConvertible<T1, T2>::value,
michael@0 424 "should not be convertible");
michael@0 425 static_assert(!IsConvertible<T1, decltype(T2::A)>::value,
michael@0 426 "should not be convertible");
michael@0 427 static_assert(!IsConvertible<T1, decltype(T2::A | T2::B)>::value,
michael@0 428 "should not be convertible");
michael@0 429
michael@0 430 static_assert(!IsConvertible<decltype(T1::A), T2>::value,
michael@0 431 "should not be convertible");
michael@0 432 static_assert(!IsConvertible<decltype(T1::A), decltype(T2::A)>::value,
michael@0 433 "should not be convertible");
michael@0 434 static_assert(!IsConvertible<decltype(T1::A), decltype(T2::A | T2::B)>::value,
michael@0 435 "should not be convertible");
michael@0 436
michael@0 437 // The following are #ifdef MOZ_HAVE_EXPLICIT_CONVERSION because
michael@0 438 // without support for explicit conversion operators, we can't easily have these
michael@0 439 // bad conversions completely removed. They still do fail to compile in practice,
michael@0 440 // but not in a way that we can static_assert on.
michael@0 441 #ifdef MOZ_HAVE_EXPLICIT_CONVERSION
michael@0 442 static_assert(!IsConvertible<decltype(T1::A | T1::B), T2>::value,
michael@0 443 "should not be convertible");
michael@0 444 static_assert(!IsConvertible<decltype(T1::A | T1::B), decltype(T2::A)>::value,
michael@0 445 "should not be convertible");
michael@0 446 static_assert(!IsConvertible<decltype(T1::A | T1::B), decltype(T2::A | T2::B)>::value,
michael@0 447 "should not be convertible");
michael@0 448 #endif
michael@0 449 }
michael@0 450
michael@0 451 MOZ_BEGIN_ENUM_CLASS(Int8EnumWithHighBits, int8_t)
michael@0 452 A = 0x20,
michael@0 453 B = 0x40
michael@0 454 MOZ_END_ENUM_CLASS(Int8EnumWithHighBits)
michael@0 455 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Int8EnumWithHighBits)
michael@0 456
michael@0 457 MOZ_BEGIN_ENUM_CLASS(Uint8EnumWithHighBits, uint8_t)
michael@0 458 A = 0x40,
michael@0 459 B = 0x80
michael@0 460 MOZ_END_ENUM_CLASS(Uint8EnumWithHighBits)
michael@0 461 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Uint8EnumWithHighBits)
michael@0 462
michael@0 463 MOZ_BEGIN_ENUM_CLASS(Int16EnumWithHighBits, int16_t)
michael@0 464 A = 0x2000,
michael@0 465 B = 0x4000
michael@0 466 MOZ_END_ENUM_CLASS(Int16EnumWithHighBits)
michael@0 467 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Int16EnumWithHighBits)
michael@0 468
michael@0 469 MOZ_BEGIN_ENUM_CLASS(Uint16EnumWithHighBits, uint16_t)
michael@0 470 A = 0x4000,
michael@0 471 B = 0x8000
michael@0 472 MOZ_END_ENUM_CLASS(Uint16EnumWithHighBits)
michael@0 473 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Uint16EnumWithHighBits)
michael@0 474
michael@0 475 MOZ_BEGIN_ENUM_CLASS(Int32EnumWithHighBits, int32_t)
michael@0 476 A = 0x20000000,
michael@0 477 B = 0x40000000
michael@0 478 MOZ_END_ENUM_CLASS(Int32EnumWithHighBits)
michael@0 479 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Int32EnumWithHighBits)
michael@0 480
michael@0 481 MOZ_BEGIN_ENUM_CLASS(Uint32EnumWithHighBits, uint32_t)
michael@0 482 A = 0x40000000u,
michael@0 483 B = 0x80000000u
michael@0 484 MOZ_END_ENUM_CLASS(Uint32EnumWithHighBits)
michael@0 485 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Uint32EnumWithHighBits)
michael@0 486
michael@0 487 MOZ_BEGIN_ENUM_CLASS(Int64EnumWithHighBits, int64_t)
michael@0 488 A = 0x2000000000000000ll,
michael@0 489 B = 0x4000000000000000ll
michael@0 490 MOZ_END_ENUM_CLASS(Int64EnumWithHighBits)
michael@0 491 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Int64EnumWithHighBits)
michael@0 492
michael@0 493 MOZ_BEGIN_ENUM_CLASS(Uint64EnumWithHighBits, uint64_t)
michael@0 494 A = 0x4000000000000000ull,
michael@0 495 B = 0x8000000000000000ull
michael@0 496 MOZ_END_ENUM_CLASS(Uint64EnumWithHighBits)
michael@0 497 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Uint64EnumWithHighBits)
michael@0 498
michael@0 499 // Checks that we don't accidentally truncate high bits by coercing to the wrong
michael@0 500 // integer type internally when implementing bitwise ops.
michael@0 501 template<typename EnumType, typename IntType>
michael@0 502 void TestIsNotTruncated()
michael@0 503 {
michael@0 504 EnumType a = EnumType::A;
michael@0 505 EnumType b = EnumType::B;
michael@0 506 MOZ_RELEASE_ASSERT(IntType(a));
michael@0 507 MOZ_RELEASE_ASSERT(IntType(b));
michael@0 508 MOZ_RELEASE_ASSERT(a | EnumType::B);
michael@0 509 MOZ_RELEASE_ASSERT(a | b);
michael@0 510 MOZ_RELEASE_ASSERT(EnumType::A | EnumType::B);
michael@0 511 EnumType c = EnumType::A | EnumType::B;
michael@0 512 MOZ_RELEASE_ASSERT(IntType(c));
michael@0 513 MOZ_RELEASE_ASSERT(c & c);
michael@0 514 MOZ_RELEASE_ASSERT(c | c);
michael@0 515 MOZ_RELEASE_ASSERT(c == (EnumType::A | EnumType::B));
michael@0 516 MOZ_RELEASE_ASSERT(a != (EnumType::A | EnumType::B));
michael@0 517 MOZ_RELEASE_ASSERT(b != (EnumType::A | EnumType::B));
michael@0 518 MOZ_RELEASE_ASSERT(c & EnumType::A);
michael@0 519 MOZ_RELEASE_ASSERT(c & EnumType::B);
michael@0 520 EnumType d = EnumType::A;
michael@0 521 d |= EnumType::B;
michael@0 522 MOZ_RELEASE_ASSERT(d == c);
michael@0 523 }
michael@0 524
michael@0 525 int
michael@0 526 main()
michael@0 527 {
michael@0 528 TestTypedEnumBasics<AutoEnum>();
michael@0 529 TestTypedEnumBasics<CharEnum>();
michael@0 530 TestTypedEnumBasics<Nested::AutoEnum>();
michael@0 531 TestTypedEnumBasics<Nested::CharEnum>();
michael@0 532
michael@0 533 TestTypedEnumBitField<AutoEnumBitField>();
michael@0 534 TestTypedEnumBitField<CharEnumBitField>();
michael@0 535 TestTypedEnumBitField<Nested::AutoEnumBitField>();
michael@0 536 TestTypedEnumBitField<Nested::CharEnumBitField>();
michael@0 537
michael@0 538 TestTypedEnumBitField<BitFieldFor_uint8_t>();
michael@0 539 TestTypedEnumBitField<BitFieldFor_int8_t>();
michael@0 540 TestTypedEnumBitField<BitFieldFor_uint16_t>();
michael@0 541 TestTypedEnumBitField<BitFieldFor_int16_t>();
michael@0 542 TestTypedEnumBitField<BitFieldFor_uint32_t>();
michael@0 543 TestTypedEnumBitField<BitFieldFor_int32_t>();
michael@0 544 TestTypedEnumBitField<BitFieldFor_uint64_t>();
michael@0 545 TestTypedEnumBitField<BitFieldFor_int64_t>();
michael@0 546 TestTypedEnumBitField<BitFieldFor_char>();
michael@0 547 TestTypedEnumBitField<BitFieldFor_signed_char>();
michael@0 548 TestTypedEnumBitField<BitFieldFor_unsigned_char>();
michael@0 549 TestTypedEnumBitField<BitFieldFor_short>();
michael@0 550 TestTypedEnumBitField<BitFieldFor_unsigned_short>();
michael@0 551 TestTypedEnumBitField<BitFieldFor_int>();
michael@0 552 TestTypedEnumBitField<BitFieldFor_unsigned_int>();
michael@0 553 TestTypedEnumBitField<BitFieldFor_long>();
michael@0 554 TestTypedEnumBitField<BitFieldFor_unsigned_long>();
michael@0 555 TestTypedEnumBitField<BitFieldFor_long_long>();
michael@0 556 TestTypedEnumBitField<BitFieldFor_unsigned_long_long>();
michael@0 557
michael@0 558 TestNoConversionsBetweenUnrelatedTypes();
michael@0 559
michael@0 560 TestIsNotTruncated<Int8EnumWithHighBits, int8_t>();
michael@0 561 TestIsNotTruncated<Int16EnumWithHighBits, int16_t>();
michael@0 562 TestIsNotTruncated<Int32EnumWithHighBits, int32_t>();
michael@0 563 TestIsNotTruncated<Int64EnumWithHighBits, int64_t>();
michael@0 564 TestIsNotTruncated<Uint8EnumWithHighBits, uint8_t>();
michael@0 565 TestIsNotTruncated<Uint16EnumWithHighBits, uint16_t>();
michael@0 566 TestIsNotTruncated<Uint32EnumWithHighBits, uint32_t>();
michael@0 567 TestIsNotTruncated<Uint64EnumWithHighBits, uint64_t>();
michael@0 568
michael@0 569 return 0;
michael@0 570 }

mercurial