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