Thu, 22 Jan 2015 13:21:57 +0100
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 | } |