Bug 1714154 [wpt PR 29187] - Fix OOF fragments to be up-to-date under certain conditi...
[gecko.git] / mfbt / tests / TestTypedEnum.cpp
blob274000aab723ec9b8939be20d4d79e3880a872df
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/Assertions.h"
8 #include "mozilla/Attributes.h"
9 #include "mozilla/TypedEnumBits.h"
11 #include <stdint.h>
12 #include <type_traits>
14 // A rough feature check for is_literal_type. Not very carefully checked.
15 // Feel free to amend as needed.
16 // We leave ANDROID out because it's using stlport which doesn't have
17 // std::is_literal_type.
18 #if __cplusplus >= 201103L && !defined(ANDROID)
19 # if defined(__clang__)
21 * Per Clang documentation, "Note that marketing version numbers should not
22 * be used to check for language features, as different vendors use different
23 * numbering schemes. Instead, use the feature checking macros."
25 # ifndef __has_extension
26 # define __has_extension \
27 __has_feature /* compatibility, for older versions of clang */
28 # endif
29 # if __has_extension(is_literal) && __has_include(<type_traits>)
30 # define MOZ_HAVE_IS_LITERAL
31 # endif
32 # elif defined(__GNUC__) || defined(_MSC_VER)
33 # define MOZ_HAVE_IS_LITERAL
34 # endif
35 #endif
37 #if defined(MOZ_HAVE_IS_LITERAL) && defined(MOZ_HAVE_CXX11_CONSTEXPR)
38 # include <type_traits>
39 template <typename T>
40 void RequireLiteralType() {
41 static_assert(std::is_literal_type<T>::value, "Expected a literal type");
43 #else // not MOZ_HAVE_IS_LITERAL
44 template <typename T>
45 void RequireLiteralType() {}
46 #endif
48 template <typename T>
49 void RequireLiteralType(const T&) {
50 RequireLiteralType<T>();
53 enum class AutoEnum { A, B = -3, C };
55 enum class CharEnum : char { A, B = 3, C };
57 enum class AutoEnumBitField { A = 0x10, B = 0x20, C };
59 enum class CharEnumBitField : char { A = 0x10, B, C = 0x40 };
61 struct Nested {
62 enum class AutoEnum { A, B, C = -1 };
64 enum class CharEnum : char { A = 4, B, C = 1 };
66 enum class AutoEnumBitField { A, B = 0x20, C };
68 enum class CharEnumBitField : char { A = 1, B = 1, C = 1 };
71 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(AutoEnumBitField)
72 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(CharEnumBitField)
73 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Nested::AutoEnumBitField)
74 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Nested::CharEnumBitField)
76 #define MAKE_STANDARD_BITFIELD_FOR_TYPE(IntType) \
77 enum class BitFieldFor_##IntType : IntType{ \
78 A = 1, \
79 B = 2, \
80 C = 4, \
81 }; \
82 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(BitFieldFor_##IntType)
84 MAKE_STANDARD_BITFIELD_FOR_TYPE(int8_t)
85 MAKE_STANDARD_BITFIELD_FOR_TYPE(uint8_t)
86 MAKE_STANDARD_BITFIELD_FOR_TYPE(int16_t)
87 MAKE_STANDARD_BITFIELD_FOR_TYPE(uint16_t)
88 MAKE_STANDARD_BITFIELD_FOR_TYPE(int32_t)
89 MAKE_STANDARD_BITFIELD_FOR_TYPE(uint32_t)
90 MAKE_STANDARD_BITFIELD_FOR_TYPE(int64_t)
91 MAKE_STANDARD_BITFIELD_FOR_TYPE(uint64_t)
92 MAKE_STANDARD_BITFIELD_FOR_TYPE(char)
93 typedef signed char signed_char;
94 MAKE_STANDARD_BITFIELD_FOR_TYPE(signed_char)
95 typedef unsigned char unsigned_char;
96 MAKE_STANDARD_BITFIELD_FOR_TYPE(unsigned_char)
97 MAKE_STANDARD_BITFIELD_FOR_TYPE(short)
98 typedef unsigned short unsigned_short;
99 MAKE_STANDARD_BITFIELD_FOR_TYPE(unsigned_short)
100 MAKE_STANDARD_BITFIELD_FOR_TYPE(int)
101 typedef unsigned int unsigned_int;
102 MAKE_STANDARD_BITFIELD_FOR_TYPE(unsigned_int)
103 MAKE_STANDARD_BITFIELD_FOR_TYPE(long)
104 typedef unsigned long unsigned_long;
105 MAKE_STANDARD_BITFIELD_FOR_TYPE(unsigned_long)
106 typedef long long long_long;
107 MAKE_STANDARD_BITFIELD_FOR_TYPE(long_long)
108 typedef unsigned long long unsigned_long_long;
109 MAKE_STANDARD_BITFIELD_FOR_TYPE(unsigned_long_long)
111 #undef MAKE_STANDARD_BITFIELD_FOR_TYPE
113 template <typename T>
114 void TestNonConvertibilityForOneType() {
115 static_assert(!std::is_convertible_v<T, bool>, "should not be convertible");
116 static_assert(!std::is_convertible_v<T, int>, "should not be convertible");
117 static_assert(!std::is_convertible_v<T, uint64_t>,
118 "should not be convertible");
120 static_assert(!std::is_convertible_v<bool, T>, "should not be convertible");
121 static_assert(!std::is_convertible_v<int, T>, "should not be convertible");
122 static_assert(!std::is_convertible_v<uint64_t, T>,
123 "should not be convertible");
126 template <typename TypedEnum>
127 void TestTypedEnumBasics() {
128 const TypedEnum a = TypedEnum::A;
129 int unused = int(a);
130 (void)unused;
131 RequireLiteralType(TypedEnum::A);
132 RequireLiteralType(a);
133 TestNonConvertibilityForOneType<TypedEnum>();
136 // Op wraps a bitwise binary operator, passed as a char template parameter,
137 // and applies it to its arguments (aT1, aT2). For example,
139 // Op<'|'>(aT1, aT2)
141 // is the same as
143 // aT1 | aT2.
145 template <char o, typename T1, typename T2>
146 auto Op(const T1& aT1, const T2& aT2)
147 -> decltype(aT1 | aT2) // See the static_assert's below --- the return type
148 // depends solely on the operands type, not on the
149 // choice of operation.
151 static_assert(std::is_same_v<decltype(aT1 | aT2), decltype(aT1 & aT2)>,
152 "binary ops should have the same result type");
153 static_assert(std::is_same_v<decltype(aT1 | aT2), decltype(aT1 ^ aT2)>,
154 "binary ops should have the same result type");
156 static_assert(o == '|' || o == '&' || o == '^',
157 "unexpected operator character");
159 return o == '|' ? aT1 | aT2 : o == '&' ? aT1 & aT2 : aT1 ^ aT2;
162 // OpAssign wraps a bitwise binary operator, passed as a char template
163 // parameter, and applies the corresponding compound-assignment operator to its
164 // arguments (aT1, aT2). For example,
166 // OpAssign<'|'>(aT1, aT2)
168 // is the same as
170 // aT1 |= aT2.
172 template <char o, typename T1, typename T2>
173 T1& OpAssign(T1& aT1, const T2& aT2) {
174 static_assert(o == '|' || o == '&' || o == '^',
175 "unexpected operator character");
177 switch (o) {
178 case '|':
179 return aT1 |= aT2;
180 case '&':
181 return aT1 &= aT2;
182 case '^':
183 return aT1 ^= aT2;
184 default:
185 MOZ_CRASH();
189 // Tests a single binary bitwise operator, using a single set of three operands.
190 // The operations tested are:
192 // result = aT1 Op aT2;
193 // result Op= aT3;
195 // Where Op is the operator specified by the char template parameter 'o' and
196 // can be any of '|', '&', '^'.
198 // Note that the operands aT1, aT2, aT3 are intentionally passed with free
199 // types (separate template parameters for each) because their type may
200 // actually be different from TypedEnum:
202 // 1) Their type could be CastableTypedEnumResult<TypedEnum> if they are
203 // the result of a bitwise operation themselves;
204 // 2) In the non-c++11 legacy path, the type of enum values is also
205 // different from TypedEnum.
207 template <typename TypedEnum, char o, typename T1, typename T2, typename T3>
208 void TestBinOp(const T1& aT1, const T2& aT2, const T3& aT3) {
209 typedef typename mozilla::detail::UnsignedIntegerTypeForEnum<TypedEnum>::Type
210 UnsignedIntegerType;
212 // Part 1:
213 // Test the bitwise binary operator i.e.
214 // result = aT1 Op aT2;
215 auto result = Op<o>(aT1, aT2);
217 typedef decltype(result) ResultType;
219 RequireLiteralType<ResultType>();
220 TestNonConvertibilityForOneType<ResultType>();
222 UnsignedIntegerType unsignedIntegerResult =
223 Op<o>(UnsignedIntegerType(aT1), UnsignedIntegerType(aT2));
225 MOZ_RELEASE_ASSERT(unsignedIntegerResult == UnsignedIntegerType(result));
226 MOZ_RELEASE_ASSERT(TypedEnum(unsignedIntegerResult) == TypedEnum(result));
227 MOZ_RELEASE_ASSERT((!unsignedIntegerResult) == (!result));
228 MOZ_RELEASE_ASSERT((!!unsignedIntegerResult) == (!!result));
229 MOZ_RELEASE_ASSERT(bool(unsignedIntegerResult) == bool(result));
231 // Part 2:
232 // Test the compound-assignment operator, i.e.
233 // result Op= aT3;
234 TypedEnum newResult = result;
235 OpAssign<o>(newResult, aT3);
236 UnsignedIntegerType unsignedIntegerNewResult = unsignedIntegerResult;
237 OpAssign<o>(unsignedIntegerNewResult, UnsignedIntegerType(aT3));
238 MOZ_RELEASE_ASSERT(TypedEnum(unsignedIntegerNewResult) == newResult);
240 // Part 3:
241 // Test additional boolean operators that we unfortunately had to add to
242 // CastableTypedEnumResult at some point to please some compiler,
243 // even though bool convertibility should have been enough.
244 MOZ_RELEASE_ASSERT(result == TypedEnum(result));
245 MOZ_RELEASE_ASSERT(!(result != TypedEnum(result)));
246 MOZ_RELEASE_ASSERT((result && true) == bool(result));
247 MOZ_RELEASE_ASSERT((result && false) == false);
248 MOZ_RELEASE_ASSERT((true && result) == bool(result));
249 MOZ_RELEASE_ASSERT((false && result && false) == false);
250 MOZ_RELEASE_ASSERT((result || false) == bool(result));
251 MOZ_RELEASE_ASSERT((result || true) == true);
252 MOZ_RELEASE_ASSERT((false || result) == bool(result));
253 MOZ_RELEASE_ASSERT((true || result) == true);
255 // Part 4:
256 // Test short-circuit evaluation.
257 auto Explode = [] {
258 // This function should never be called. Return an arbitrary value.
259 MOZ_RELEASE_ASSERT(false);
260 return false;
262 if (result) {
263 MOZ_RELEASE_ASSERT(result || Explode());
264 MOZ_RELEASE_ASSERT(!(!result && Explode()));
265 } else {
266 MOZ_RELEASE_ASSERT(!(result && Explode()));
267 MOZ_RELEASE_ASSERT(!result || Explode());
271 // Similar to TestBinOp but testing the unary ~ operator.
272 template <typename TypedEnum, typename T>
273 void TestTilde(const T& aT) {
274 typedef typename mozilla::detail::UnsignedIntegerTypeForEnum<TypedEnum>::Type
275 UnsignedIntegerType;
277 auto result = ~aT;
279 typedef decltype(result) ResultType;
281 RequireLiteralType<ResultType>();
282 TestNonConvertibilityForOneType<ResultType>();
284 UnsignedIntegerType unsignedIntegerResult = ~(UnsignedIntegerType(aT));
286 MOZ_RELEASE_ASSERT(unsignedIntegerResult == UnsignedIntegerType(result));
287 MOZ_RELEASE_ASSERT(TypedEnum(unsignedIntegerResult) == TypedEnum(result));
288 MOZ_RELEASE_ASSERT((!unsignedIntegerResult) == (!result));
289 MOZ_RELEASE_ASSERT((!!unsignedIntegerResult) == (!!result));
290 MOZ_RELEASE_ASSERT(bool(unsignedIntegerResult) == bool(result));
293 // Helper dispatching a given triple of operands to all operator-specific
294 // testing functions.
295 template <typename TypedEnum, typename T1, typename T2, typename T3>
296 void TestAllOpsForGivenOperands(const T1& aT1, const T2& aT2, const T3& aT3) {
297 TestBinOp<TypedEnum, '|'>(aT1, aT2, aT3);
298 TestBinOp<TypedEnum, '&'>(aT1, aT2, aT3);
299 TestBinOp<TypedEnum, '^'>(aT1, aT2, aT3);
300 TestTilde<TypedEnum>(aT1);
303 // Helper building various triples of operands using a given operator,
304 // and testing all operators with them.
305 template <typename TypedEnum, char o>
306 void TestAllOpsForOperandsBuiltUsingGivenOp() {
307 // The type of enum values like TypedEnum::A may be different from
308 // TypedEnum. That is the case in the legacy non-C++11 path. We want to
309 // ensure good test coverage even when these two types are distinct.
310 // To that effect, we have both 'auto' typed variables, preserving the
311 // original type of enum values, and 'plain' typed variables, that
312 // are plain TypedEnum's.
314 const TypedEnum a_plain = TypedEnum::A;
315 const TypedEnum b_plain = TypedEnum::B;
316 const TypedEnum c_plain = TypedEnum::C;
318 auto a_auto = TypedEnum::A;
319 auto b_auto = TypedEnum::B;
320 auto c_auto = TypedEnum::C;
322 auto ab_plain = Op<o>(a_plain, b_plain);
323 auto bc_plain = Op<o>(b_plain, c_plain);
324 auto ab_auto = Op<o>(a_auto, b_auto);
325 auto bc_auto = Op<o>(b_auto, c_auto);
327 // On each row below, we pass a triple of operands. Keep in mind that this
328 // is going to be received as (aT1, aT2, aT3) and the actual tests performed
329 // will be of the form
331 // result = aT1 Op aT2;
332 // result Op= aT3;
334 // For this reason, we carefully ensure that the values of (aT1, aT2)
335 // systematically cover all types of such pairs; to limit complexity,
336 // we are not so careful with aT3, and we just try to pass aT3's
337 // that may lead to nontrivial bitwise operations.
338 TestAllOpsForGivenOperands<TypedEnum>(a_plain, b_plain, c_plain);
339 TestAllOpsForGivenOperands<TypedEnum>(a_plain, bc_plain, b_auto);
340 TestAllOpsForGivenOperands<TypedEnum>(ab_plain, c_plain, a_plain);
341 TestAllOpsForGivenOperands<TypedEnum>(ab_plain, bc_plain, a_auto);
343 TestAllOpsForGivenOperands<TypedEnum>(a_plain, b_auto, c_plain);
344 TestAllOpsForGivenOperands<TypedEnum>(a_plain, bc_auto, b_auto);
345 TestAllOpsForGivenOperands<TypedEnum>(ab_plain, c_auto, a_plain);
346 TestAllOpsForGivenOperands<TypedEnum>(ab_plain, bc_auto, a_auto);
348 TestAllOpsForGivenOperands<TypedEnum>(a_auto, b_plain, c_plain);
349 TestAllOpsForGivenOperands<TypedEnum>(a_auto, bc_plain, b_auto);
350 TestAllOpsForGivenOperands<TypedEnum>(ab_auto, c_plain, a_plain);
351 TestAllOpsForGivenOperands<TypedEnum>(ab_auto, bc_plain, a_auto);
353 TestAllOpsForGivenOperands<TypedEnum>(a_auto, b_auto, c_plain);
354 TestAllOpsForGivenOperands<TypedEnum>(a_auto, bc_auto, b_auto);
355 TestAllOpsForGivenOperands<TypedEnum>(ab_auto, c_auto, a_plain);
356 TestAllOpsForGivenOperands<TypedEnum>(ab_auto, bc_auto, a_auto);
359 // Tests all bitwise operations on a given TypedEnum bitfield.
360 template <typename TypedEnum>
361 void TestTypedEnumBitField() {
362 TestTypedEnumBasics<TypedEnum>();
364 TestAllOpsForOperandsBuiltUsingGivenOp<TypedEnum, '|'>();
365 TestAllOpsForOperandsBuiltUsingGivenOp<TypedEnum, '&'>();
366 TestAllOpsForOperandsBuiltUsingGivenOp<TypedEnum, '^'>();
369 // Checks that enum bitwise expressions have the same non-convertibility
370 // properties as c++11 enum classes do, i.e. not implicitly convertible to
371 // anything (though *explicitly* convertible).
372 void TestNoConversionsBetweenUnrelatedTypes() {
373 // Two typed enum classes having the same underlying integer type, to ensure
374 // that we would catch bugs accidentally allowing conversions in that case.
375 typedef CharEnumBitField T1;
376 typedef Nested::CharEnumBitField T2;
378 static_assert(!std::is_convertible_v<T1, T2>, "should not be convertible");
379 static_assert(!std::is_convertible_v<T1, decltype(T2::A)>,
380 "should not be convertible");
381 static_assert(!std::is_convertible_v<T1, decltype(T2::A | T2::B)>,
382 "should not be convertible");
384 static_assert(!std::is_convertible_v<decltype(T1::A), T2>,
385 "should not be convertible");
386 static_assert(!std::is_convertible_v<decltype(T1::A), decltype(T2::A)>,
387 "should not be convertible");
388 static_assert(
389 !std::is_convertible_v<decltype(T1::A), decltype(T2::A | T2::B)>,
390 "should not be convertible");
392 static_assert(!std::is_convertible_v<decltype(T1::A | T1::B), T2>,
393 "should not be convertible");
394 static_assert(
395 !std::is_convertible_v<decltype(T1::A | T1::B), decltype(T2::A)>,
396 "should not be convertible");
397 static_assert(
398 !std::is_convertible_v<decltype(T1::A | T1::B), decltype(T2::A | T2::B)>,
399 "should not be convertible");
402 enum class Int8EnumWithHighBits : int8_t { A = 0x20, B = 0x40 };
403 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Int8EnumWithHighBits)
405 enum class Uint8EnumWithHighBits : uint8_t { A = 0x40, B = 0x80 };
406 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Uint8EnumWithHighBits)
408 enum class Int16EnumWithHighBits : int16_t { A = 0x2000, B = 0x4000 };
409 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Int16EnumWithHighBits)
411 enum class Uint16EnumWithHighBits : uint16_t { A = 0x4000, B = 0x8000 };
412 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Uint16EnumWithHighBits)
414 enum class Int32EnumWithHighBits : int32_t { A = 0x20000000, B = 0x40000000 };
415 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Int32EnumWithHighBits)
417 enum class Uint32EnumWithHighBits : uint32_t {
418 A = 0x40000000u,
419 B = 0x80000000u
421 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Uint32EnumWithHighBits)
423 enum class Int64EnumWithHighBits : int64_t {
424 A = 0x2000000000000000ll,
425 B = 0x4000000000000000ll
427 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Int64EnumWithHighBits)
429 enum class Uint64EnumWithHighBits : uint64_t {
430 A = 0x4000000000000000ull,
431 B = 0x8000000000000000ull
433 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Uint64EnumWithHighBits)
435 // Checks that we don't accidentally truncate high bits by coercing to the wrong
436 // integer type internally when implementing bitwise ops.
437 template <typename EnumType, typename IntType>
438 void TestIsNotTruncated() {
439 EnumType a = EnumType::A;
440 EnumType b = EnumType::B;
441 MOZ_RELEASE_ASSERT(IntType(a));
442 MOZ_RELEASE_ASSERT(IntType(b));
443 MOZ_RELEASE_ASSERT(a | EnumType::B);
444 MOZ_RELEASE_ASSERT(a | b);
445 MOZ_RELEASE_ASSERT(EnumType::A | EnumType::B);
446 EnumType c = EnumType::A | EnumType::B;
447 MOZ_RELEASE_ASSERT(IntType(c));
448 MOZ_RELEASE_ASSERT(c & c);
449 MOZ_RELEASE_ASSERT(c | c);
450 MOZ_RELEASE_ASSERT(c == (EnumType::A | EnumType::B));
451 MOZ_RELEASE_ASSERT(a != (EnumType::A | EnumType::B));
452 MOZ_RELEASE_ASSERT(b != (EnumType::A | EnumType::B));
453 MOZ_RELEASE_ASSERT(c & EnumType::A);
454 MOZ_RELEASE_ASSERT(c & EnumType::B);
455 EnumType d = EnumType::A;
456 d |= EnumType::B;
457 MOZ_RELEASE_ASSERT(d == c);
460 int main() {
461 TestTypedEnumBasics<AutoEnum>();
462 TestTypedEnumBasics<CharEnum>();
463 TestTypedEnumBasics<Nested::AutoEnum>();
464 TestTypedEnumBasics<Nested::CharEnum>();
466 TestTypedEnumBitField<AutoEnumBitField>();
467 TestTypedEnumBitField<CharEnumBitField>();
468 TestTypedEnumBitField<Nested::AutoEnumBitField>();
469 TestTypedEnumBitField<Nested::CharEnumBitField>();
471 TestTypedEnumBitField<BitFieldFor_uint8_t>();
472 TestTypedEnumBitField<BitFieldFor_int8_t>();
473 TestTypedEnumBitField<BitFieldFor_uint16_t>();
474 TestTypedEnumBitField<BitFieldFor_int16_t>();
475 TestTypedEnumBitField<BitFieldFor_uint32_t>();
476 TestTypedEnumBitField<BitFieldFor_int32_t>();
477 TestTypedEnumBitField<BitFieldFor_uint64_t>();
478 TestTypedEnumBitField<BitFieldFor_int64_t>();
479 TestTypedEnumBitField<BitFieldFor_char>();
480 TestTypedEnumBitField<BitFieldFor_signed_char>();
481 TestTypedEnumBitField<BitFieldFor_unsigned_char>();
482 TestTypedEnumBitField<BitFieldFor_short>();
483 TestTypedEnumBitField<BitFieldFor_unsigned_short>();
484 TestTypedEnumBitField<BitFieldFor_int>();
485 TestTypedEnumBitField<BitFieldFor_unsigned_int>();
486 TestTypedEnumBitField<BitFieldFor_long>();
487 TestTypedEnumBitField<BitFieldFor_unsigned_long>();
488 TestTypedEnumBitField<BitFieldFor_long_long>();
489 TestTypedEnumBitField<BitFieldFor_unsigned_long_long>();
491 TestNoConversionsBetweenUnrelatedTypes();
493 TestIsNotTruncated<Int8EnumWithHighBits, int8_t>();
494 TestIsNotTruncated<Int16EnumWithHighBits, int16_t>();
495 TestIsNotTruncated<Int32EnumWithHighBits, int32_t>();
496 TestIsNotTruncated<Int64EnumWithHighBits, int64_t>();
497 TestIsNotTruncated<Uint8EnumWithHighBits, uint8_t>();
498 TestIsNotTruncated<Uint16EnumWithHighBits, uint16_t>();
499 TestIsNotTruncated<Uint32EnumWithHighBits, uint32_t>();
500 TestIsNotTruncated<Uint64EnumWithHighBits, uint64_t>();
502 return 0;