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"
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 */
29 # if __has_extension(is_literal) && __has_include(<type_traits>)
30 # define MOZ_HAVE_IS_LITERAL
32 # elif defined(__GNUC__) || defined(_MSC_VER)
33 # define MOZ_HAVE_IS_LITERAL
37 #if defined(MOZ_HAVE_IS_LITERAL) && defined(MOZ_HAVE_CXX11_CONSTEXPR)
38 # include <type_traits>
40 void RequireLiteralType() {
41 static_assert(std::is_literal_type
<T
>::value
, "Expected a literal type");
43 #else // not MOZ_HAVE_IS_LITERAL
45 void RequireLiteralType() {}
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 };
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{ \
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
;
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,
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)
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");
189 // Tests a single binary bitwise operator, using a single set of three operands.
190 // The operations tested are:
192 // result = aT1 Op aT2;
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
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
));
232 // Test the compound-assignment operator, i.e.
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
);
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);
256 // Test short-circuit evaluation.
258 // This function should never be called. Return an arbitrary value.
259 MOZ_RELEASE_ASSERT(false);
263 MOZ_RELEASE_ASSERT(result
|| Explode());
264 MOZ_RELEASE_ASSERT(!(!result
&& Explode()));
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
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;
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");
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");
395 !std::is_convertible_v
<decltype(T1::A
| T1::B
), decltype(T2::A
)>,
396 "should not be convertible");
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 {
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
;
457 MOZ_RELEASE_ASSERT(d
== c
);
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>();