1 /* Self tests for enum-flags for GDB, the GNU debugger. 2 3 Copyright (C) 2016-2024 Free Software Foundation, Inc. 4 5 This file is part of GDB. 6 7 This program is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 3 of the License, or 10 (at your option) any later version. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program. If not, see <http://www.gnu.org/licenses/>. */ 19 20 #include "gdbsupport/enum-flags.h" 21 #include "gdbsupport/valid-expr.h" 22 #include "gdbsupport/selftest.h" 23 24 namespace selftests { 25 namespace enum_flags_tests { 26 27 /* The (real) enum types used in CHECK_VALID. Their names match the 28 template parameter names of the templates defined by CHECK_VALID to 29 make it simpler to use. They could be named differently. */ 30 31 /* A "real enum". */ 32 enum RE 33 { 34 RE_FLAG1 = 1 << 1, 35 RE_FLAG2 = 1 << 2, 36 }; 37 38 /* Another "real enum". */ 39 enum RE2 40 { 41 RE2_FLAG1 = 1 << 1, 42 RE2_FLAG2 = 1 << 2, 43 }; 44 45 /* An unsigned "real enum". */ 46 enum URE : unsigned 47 { 48 URE_FLAG1 = 1 << 1, 49 URE_FLAG2 = 1 << 2, 50 URE_FLAG3 = 0xffffffff, 51 }; 52 53 /* A non-flags enum. */ 54 enum NF 55 { 56 NF_FLAG1 = 1 << 1, 57 NF_FLAG2 = 1 << 2, 58 }; 59 60 /* The corresponding "enum flags" types. */ 61 DEF_ENUM_FLAGS_TYPE (RE, EF); 62 DEF_ENUM_FLAGS_TYPE (RE2, EF2); 63 DEF_ENUM_FLAGS_TYPE (URE, UEF); 64 65 /* So that std::vectors of types that have enum_flags fields can 66 reallocate efficiently memcpy. */ 67 static_assert (std::is_trivially_copyable<EF>::value); 68 69 /* A couple globals used as lvalues in the CHECK_VALID expressions 70 below. Their names (and types) match the uppercase type names 71 exposed by CHECK_VALID just to make the expressions easier to 72 follow. */ 73 static RE re ATTRIBUTE_UNUSED; 74 static EF ef ATTRIBUTE_UNUSED; 75 76 /* First, compile-time tests that: 77 78 - make sure that incorrect operations with mismatching enum types 79 are caught at compile time. 80 81 - make sure that the same operations but involving the right enum 82 types do compile and that they return the correct type. 83 */ 84 85 #define CHECK_VALID(VALID, EXPR_TYPE, EXPR) \ 86 CHECK_VALID_EXPR_6 (EF, RE, EF2, RE2, UEF, URE, VALID, EXPR_TYPE, EXPR) 87 88 typedef std::underlying_type<RE>::type und; 89 90 /* Test construction / conversion from/to different types. */ 91 92 /* RE/EF -> underlying (explicit) */ 93 CHECK_VALID (true, und, und (RE ())) 94 CHECK_VALID (true, und, und (EF ())) 95 96 /* RE/EF -> int (explicit) */ 97 CHECK_VALID (true, int, int (RE ())) 98 CHECK_VALID (true, int, int (EF ())) 99 100 /* other -> RE */ 101 102 /* You can construct a raw enum value from an int explicitly to punch 103 a hole in the type system if need to. */ 104 CHECK_VALID (true, RE, RE (1)) 105 CHECK_VALID (true, RE, RE (RE2 ())) 106 CHECK_VALID (false, void, RE (EF2 ())) 107 CHECK_VALID (true, RE, RE (RE ())) 108 CHECK_VALID (false, void, RE (EF ())) 109 110 /* other -> EF. */ 111 112 /* As expected, enum-flags is a stronger type than the backing raw 113 enum. Unlike with raw enums, you can't construct an enum flags 114 from an integer nor from an unrelated enum type explicitly. Add an 115 intermediate conversion via the raw enum if you really need it. */ 116 CHECK_VALID (false, void, EF (1)) 117 CHECK_VALID (false, void, EF (1u)) 118 CHECK_VALID (false, void, EF (RE2 ())) 119 CHECK_VALID (false, void, EF (EF2 ())) 120 CHECK_VALID (true, EF, EF (RE ())) 121 CHECK_VALID (true, EF, EF (EF ())) 122 123 /* Test operators. */ 124 125 /* operator OP (raw_enum, int) */ 126 127 CHECK_VALID (false, void, RE () | 1) 128 CHECK_VALID (false, void, RE () & 1) 129 CHECK_VALID (false, void, RE () ^ 1) 130 131 /* operator OP (int, raw_enum) */ 132 133 CHECK_VALID (false, void, 1 | RE ()) 134 CHECK_VALID (false, void, 1 & RE ()) 135 CHECK_VALID (false, void, 1 ^ RE ()) 136 137 /* operator OP (enum_flags, int) */ 138 139 CHECK_VALID (false, void, EF () | 1) 140 CHECK_VALID (false, void, EF () & 1) 141 CHECK_VALID (false, void, EF () ^ 1) 142 143 /* operator OP (int, enum_flags) */ 144 145 CHECK_VALID (false, void, 1 | EF ()) 146 CHECK_VALID (false, void, 1 & EF ()) 147 CHECK_VALID (false, void, 1 ^ EF ()) 148 149 /* operator OP (raw_enum, raw_enum) */ 150 151 CHECK_VALID (false, void, RE () | RE2 ()) 152 CHECK_VALID (false, void, RE () & RE2 ()) 153 CHECK_VALID (false, void, RE () ^ RE2 ()) 154 CHECK_VALID (true, RE, RE () | RE ()) 155 CHECK_VALID (true, RE, RE () & RE ()) 156 CHECK_VALID (true, RE, RE () ^ RE ()) 157 158 /* operator OP (enum_flags, raw_enum) */ 159 160 CHECK_VALID (false, void, EF () | RE2 ()) 161 CHECK_VALID (false, void, EF () & RE2 ()) 162 CHECK_VALID (false, void, EF () ^ RE2 ()) 163 CHECK_VALID (true, EF, EF () | RE ()) 164 CHECK_VALID (true, EF, EF () & RE ()) 165 CHECK_VALID (true, EF, EF () ^ RE ()) 166 167 /* operator OP= (raw_enum, raw_enum), rvalue ref on the lhs. */ 168 169 CHECK_VALID (false, void, RE () |= RE2 ()) 170 CHECK_VALID (false, void, RE () &= RE2 ()) 171 CHECK_VALID (false, void, RE () ^= RE2 ()) 172 CHECK_VALID (false, void, RE () |= RE ()) 173 CHECK_VALID (false, void, RE () &= RE ()) 174 CHECK_VALID (false, void, RE () ^= RE ()) 175 176 /* operator OP= (raw_enum, raw_enum), lvalue ref on the lhs. */ 177 178 CHECK_VALID (false, void, re |= RE2 ()) 179 CHECK_VALID (false, void, re &= RE2 ()) 180 CHECK_VALID (false, void, re ^= RE2 ()) 181 CHECK_VALID (true, RE&, re |= RE ()) 182 CHECK_VALID (true, RE&, re &= RE ()) 183 CHECK_VALID (true, RE&, re ^= RE ()) 184 185 /* operator OP= (enum_flags, raw_enum), rvalue ref on the lhs. */ 186 187 CHECK_VALID (false, void, EF () |= RE2 ()) 188 CHECK_VALID (false, void, EF () &= RE2 ()) 189 CHECK_VALID (false, void, EF () ^= RE2 ()) 190 CHECK_VALID (false, void, EF () |= RE ()) 191 CHECK_VALID (false, void, EF () &= RE ()) 192 CHECK_VALID (false, void, EF () ^= RE ()) 193 194 /* operator OP= (enum_flags, raw_enum), lvalue ref on the lhs. */ 195 196 CHECK_VALID (false, void, ef |= RE2 ()) 197 CHECK_VALID (false, void, ef &= RE2 ()) 198 CHECK_VALID (false, void, ef ^= RE2 ()) 199 CHECK_VALID (true, EF&, ef |= EF ()) 200 CHECK_VALID (true, EF&, ef &= EF ()) 201 CHECK_VALID (true, EF&, ef ^= EF ()) 202 203 /* operator OP= (enum_flags, enum_flags), rvalue ref on the lhs. */ 204 205 CHECK_VALID (false, void, EF () |= EF2 ()) 206 CHECK_VALID (false, void, EF () &= EF2 ()) 207 CHECK_VALID (false, void, EF () ^= EF2 ()) 208 CHECK_VALID (false, void, EF () |= EF ()) 209 CHECK_VALID (false, void, EF () &= EF ()) 210 CHECK_VALID (false, void, EF () ^= EF ()) 211 212 /* operator OP= (enum_flags, enum_flags), lvalue ref on the lhs. */ 213 214 CHECK_VALID (false, void, ef |= EF2 ()) 215 CHECK_VALID (false, void, ef &= EF2 ()) 216 CHECK_VALID (false, void, ef ^= EF2 ()) 217 CHECK_VALID (true, EF&, ef |= EF ()) 218 CHECK_VALID (true, EF&, ef &= EF ()) 219 CHECK_VALID (true, EF&, ef ^= EF ()) 220 221 /* operator~ (raw_enum) */ 222 223 CHECK_VALID (false, void, ~RE ()) 224 CHECK_VALID (true, URE, ~URE ()) 225 226 /* operator~ (enum_flags) */ 227 228 CHECK_VALID (false, void, ~EF ()) 229 CHECK_VALID (true, UEF, ~UEF ()) 230 231 /* Check ternary operator. This exercises implicit conversions. */ 232 233 CHECK_VALID (true, EF, true ? EF () : RE ()) 234 CHECK_VALID (true, EF, true ? RE () : EF ()) 235 236 /* These are valid, but it's not a big deal since you won't be able to 237 assign the resulting integer to an enum or an enum_flags without a 238 cast. 239 240 The latter two tests are disabled on older GCCs because they 241 incorrectly fail with gcc 4.8 and 4.9 at least. Running the test 242 outside a SFINAE context shows: 243 244 invalid user-defined conversion from EF to RE2 245 246 They've been confirmed to compile/pass with gcc 5.3, gcc 7.1 and 247 clang 3.7. */ 248 249 CHECK_VALID (true, int, true ? EF () : EF2 ()) 250 CHECK_VALID (true, int, true ? EF2 () : EF ()) 251 CHECK_VALID (true, int, true ? EF () : RE2 ()) 252 CHECK_VALID (true, int, true ? RE2 () : EF ()) 253 254 /* Same, but with an unsigned enum. */ 255 256 typedef unsigned int uns; 257 258 CHECK_VALID (true, uns, true ? EF () : UEF ()) 259 CHECK_VALID (true, uns, true ? UEF () : EF ()) 260 CHECK_VALID (true, uns, true ? EF () : URE ()) 261 CHECK_VALID (true, uns, true ? URE () : EF ()) 262 263 /* Unfortunately this can't work due to the way C++ computes the 264 return type of the ternary conditional operator. int isn't 265 implicitly convertible to the raw enum type, so the type of the 266 expression is int. And then int is not implicitly convertible to 267 enum_flags. 268 269 GCC 4.8 fails to compile this test with: 270 error: operands to ?: have different types enum_flags<RE> and int 271 Confirmed to work with gcc 4.9, 5.3 and clang 3.7. 272 */ 273 CHECK_VALID (false, void, true ? EF () : 0) 274 CHECK_VALID (false, void, true ? 0 : EF ()) 275 276 /* Check that the ++/--/<</>>/<<=/>>= operators are deleted. */ 277 278 CHECK_VALID (false, void, RE ()++) 279 CHECK_VALID (false, void, ++RE ()) 280 CHECK_VALID (false, void, --RE ()) 281 CHECK_VALID (false, void, RE ()--) 282 283 CHECK_VALID (false, void, RE () << 1) 284 CHECK_VALID (false, void, RE () >> 1) 285 CHECK_VALID (false, void, EF () << 1) 286 CHECK_VALID (false, void, EF () >> 1) 287 288 CHECK_VALID (false, void, RE () <<= 1) 289 CHECK_VALID (false, void, RE () >>= 1) 290 CHECK_VALID (false, void, EF () <<= 1) 291 CHECK_VALID (false, void, EF () >>= 1) 292 293 /* Test comparison operators. */ 294 295 CHECK_VALID (false, void, EF () == EF2 ()) 296 CHECK_VALID (false, void, EF () == RE2 ()) 297 CHECK_VALID (false, void, RE () == EF2 ()) 298 299 CHECK_VALID (true, bool, EF (RE (1)) == EF (RE (1))) 300 CHECK_VALID (true, bool, EF (RE (1)) == RE (1)) 301 CHECK_VALID (true, bool, RE (1) == EF (RE (1))) 302 303 CHECK_VALID (false, void, EF () != EF2 ()) 304 CHECK_VALID (false, void, EF () != RE2 ()) 305 CHECK_VALID (false, void, RE () != EF2 ()) 306 307 /* Disable -Wenum-compare due to: 308 309 Clang: 310 311 "error: comparison of two values with different enumeration types 312 [-Werror,-Wenum-compare]" 313 314 GCC: 315 316 "error: comparison between enum selftests::enum_flags_tests::RE 317 and enum selftests::enum_flags_tests::RE2 318 [-Werror=enum-compare]" 319 320 Not a big deal since misuses like these in GDB will be caught by 321 -Werror anyway. This check is here mainly for completeness. */ 322 #if defined __GNUC__ 323 # pragma GCC diagnostic push 324 # pragma GCC diagnostic ignored "-Wenum-compare" 325 #endif 326 CHECK_VALID (true, bool, RE () == RE2 ()) 327 CHECK_VALID (true, bool, RE () != RE2 ()) 328 #if defined __GNUC__ 329 # pragma GCC diagnostic pop 330 #endif 331 332 CHECK_VALID (true, bool, EF (RE (1)) != EF (RE (2))) 333 CHECK_VALID (true, bool, EF (RE (1)) != RE (2)) 334 CHECK_VALID (true, bool, RE (1) != EF (RE (2))) 335 336 CHECK_VALID (true, bool, EF () == 0) 337 338 /* Check we didn't disable/delete comparison between non-flags enums 339 and unrelated types by mistake. */ 340 CHECK_VALID (true, bool, NF (1) == NF (1)) 341 CHECK_VALID (true, bool, NF (1) == int (1)) 342 CHECK_VALID (true, bool, NF (1) == char (1)) 343 344 /* -------------------------------------------------------------------- */ 345 346 /* Follows misc tests that exercise the API. Some are compile time, 347 when possible, others are run time. */ 348 349 enum test_flag 350 { 351 FLAG1 = 1 << 0, 352 FLAG2 = 1 << 1, 353 FLAG3 = 1 << 2, 354 FLAG4 = 1 << 3, 355 }; 356 357 enum test_uflag : unsigned 358 { 359 UFLAG1 = 1 << 0, 360 UFLAG2 = 1 << 1, 361 UFLAG3 = 1 << 2, 362 UFLAG4 = 1 << 3, 363 }; 364 365 DEF_ENUM_FLAGS_TYPE (test_flag, test_flags); 366 DEF_ENUM_FLAGS_TYPE (test_uflag, test_uflags); 367 368 /* to_string enumerator->string mapping functions used to test 369 enum_flags::to_string. These intentionally miss mapping a couple 370 enumerators each (xFLAG2, xFLAG4). */ 371 372 static std::string 373 to_string_flags (test_flags flags) 374 { 375 static constexpr test_flags::string_mapping mapping[] = { 376 MAP_ENUM_FLAG (FLAG1), 377 MAP_ENUM_FLAG (FLAG3), 378 }; 379 return flags.to_string (mapping); 380 } 381 382 static std::string 383 to_string_uflags (test_uflags flags) 384 { 385 static constexpr test_uflags::string_mapping mapping[] = { 386 MAP_ENUM_FLAG (UFLAG1), 387 MAP_ENUM_FLAG (UFLAG3), 388 }; 389 return flags.to_string (mapping); 390 } 391 392 static void 393 self_test () 394 { 395 /* Check that default construction works. */ 396 { 397 constexpr test_flags f; 398 399 static_assert (f == 0); 400 } 401 402 /* Check that assignment from zero works. */ 403 { 404 test_flags f (FLAG1); 405 406 SELF_CHECK (f == FLAG1); 407 408 f = 0; 409 410 SELF_CHECK (f == 0); 411 } 412 413 /* Check that construction from zero works. */ 414 { 415 constexpr test_flags zero1 = 0; 416 constexpr test_flags zero2 (0); 417 constexpr test_flags zero3 {0}; 418 constexpr test_flags zero4 = {0}; 419 420 static_assert (zero1 == 0); 421 static_assert (zero2 == 0); 422 static_assert (zero3 == 0); 423 static_assert (zero4 == 0); 424 } 425 426 /* Check construction from enum value. */ 427 { 428 static_assert (test_flags (FLAG1) == FLAG1); 429 static_assert (test_flags (FLAG2) != FLAG1); 430 } 431 432 /* Check copy/assignment. */ 433 { 434 constexpr test_flags src = FLAG1; 435 436 constexpr test_flags f1 = src; 437 constexpr test_flags f2 (src); 438 constexpr test_flags f3 {src}; 439 constexpr test_flags f4 = {src}; 440 441 static_assert (f1 == FLAG1); 442 static_assert (f2 == FLAG1); 443 static_assert (f3 == FLAG1); 444 static_assert (f4 == FLAG1); 445 } 446 447 /* Check moving. */ 448 { 449 test_flags src = FLAG1; 450 test_flags dst = 0; 451 452 dst = std::move (src); 453 SELF_CHECK (dst == FLAG1); 454 } 455 456 /* Check construction from an 'or' of multiple bits. For this to 457 work, operator| must be overridden to return an enum type. The 458 builtin version would return int instead and then the conversion 459 to test_flags would fail. */ 460 { 461 constexpr test_flags f = FLAG1 | FLAG2; 462 static_assert (f == (FLAG1 | FLAG2)); 463 } 464 465 /* Similarly, check that "FLAG1 | FLAG2" on the rhs of an assignment 466 operator works. */ 467 { 468 test_flags f = 0; 469 f |= FLAG1 | FLAG2; 470 SELF_CHECK (f == (FLAG1 | FLAG2)); 471 472 f &= FLAG1 | FLAG2; 473 SELF_CHECK (f == (FLAG1 | FLAG2)); 474 475 f ^= FLAG1 | FLAG2; 476 SELF_CHECK (f == 0); 477 } 478 479 /* Check explicit conversion to int works. */ 480 { 481 constexpr int some_bits (FLAG1 | FLAG2); 482 483 /* And comparison with int works too. */ 484 static_assert (some_bits == (FLAG1 | FLAG2)); 485 static_assert (some_bits == test_flags (FLAG1 | FLAG2)); 486 } 487 488 /* Check operator| and operator|=. Particularly interesting is 489 making sure that putting the enum value on the lhs side of the 490 expression works (FLAG | f). */ 491 { 492 test_flags f = FLAG1; 493 f |= FLAG2; 494 SELF_CHECK (f == (FLAG1 | FLAG2)); 495 } 496 { 497 test_flags f = FLAG1; 498 f = f | FLAG2; 499 SELF_CHECK (f == (FLAG1 | FLAG2)); 500 } 501 { 502 test_flags f = FLAG1; 503 f = FLAG2 | f; 504 SELF_CHECK (f == (FLAG1 | FLAG2)); 505 } 506 507 /* Check the &/&= operators. */ 508 { 509 test_flags f = FLAG1 & FLAG2; 510 SELF_CHECK (f == 0); 511 512 f = FLAG1 | FLAG2; 513 f &= FLAG2; 514 SELF_CHECK (f == FLAG2); 515 516 f = FLAG1 | FLAG2; 517 f = f & FLAG2; 518 SELF_CHECK (f == FLAG2); 519 520 f = FLAG1 | FLAG2; 521 f = FLAG2 & f; 522 SELF_CHECK (f == FLAG2); 523 } 524 525 /* Check the ^/^= operators. */ 526 { 527 constexpr test_flags f = FLAG1 ^ FLAG2; 528 static_assert (f == (FLAG1 ^ FLAG2)); 529 } 530 531 { 532 test_flags f = FLAG1 ^ FLAG2; 533 f ^= FLAG3; 534 SELF_CHECK (f == (FLAG1 | FLAG2 | FLAG3)); 535 f = f ^ FLAG3; 536 SELF_CHECK (f == (FLAG1 | FLAG2)); 537 f = FLAG3 ^ f; 538 SELF_CHECK (f == (FLAG1 | FLAG2 | FLAG3)); 539 } 540 541 /* Check operator~. Note this only compiles with unsigned 542 flags. */ 543 { 544 constexpr test_uflags f1 = ~UFLAG1; 545 constexpr test_uflags f2 = ~f1; 546 static_assert (f2 == UFLAG1); 547 } 548 549 /* Check the ternary operator. */ 550 551 { 552 /* raw enum, raw enum */ 553 constexpr test_flags f1 = true ? FLAG1 : FLAG2; 554 static_assert (f1 == FLAG1); 555 constexpr test_flags f2 = false ? FLAG1 : FLAG2; 556 static_assert (f2 == FLAG2); 557 } 558 559 { 560 /* enum flags, raw enum */ 561 constexpr test_flags src = FLAG1; 562 constexpr test_flags f1 = true ? src : FLAG2; 563 static_assert (f1 == FLAG1); 564 constexpr test_flags f2 = false ? src : FLAG2; 565 static_assert (f2 == FLAG2); 566 } 567 568 { 569 /* enum flags, enum flags */ 570 constexpr test_flags src1 = FLAG1; 571 constexpr test_flags src2 = FLAG2; 572 constexpr test_flags f1 = true ? src1 : src2; 573 static_assert (f1 == src1); 574 constexpr test_flags f2 = false ? src1 : src2; 575 static_assert (f2 == src2); 576 } 577 578 /* Check that we can use flags in switch expressions (requires 579 unambiguous conversion to integer). Also check that we can use 580 operator| in switch cases, where only constants are allowed. 581 This should work because operator| is constexpr. */ 582 { 583 test_flags f = FLAG1 | FLAG2; 584 bool ok = false; 585 586 switch (f) 587 { 588 case FLAG1: 589 break; 590 case FLAG2: 591 break; 592 case FLAG1 | FLAG2: 593 ok = true; 594 break; 595 } 596 597 SELF_CHECK (ok); 598 } 599 600 /* Check string conversion. */ 601 { 602 SELF_CHECK (to_string_uflags (0) 603 == "0x0 []"); 604 SELF_CHECK (to_string_uflags (UFLAG1) 605 == "0x1 [UFLAG1]"); 606 SELF_CHECK (to_string_uflags (UFLAG1 | UFLAG3) 607 == "0x5 [UFLAG1 UFLAG3]"); 608 SELF_CHECK (to_string_uflags (UFLAG1 | UFLAG2 | UFLAG3) 609 == "0x7 [UFLAG1 UFLAG3 0x2]"); 610 SELF_CHECK (to_string_uflags (UFLAG2) 611 == "0x2 [0x2]"); 612 /* Check that even with multiple unmapped flags, we only print one 613 unmapped hex number (0xa, in this case). */ 614 SELF_CHECK (to_string_uflags (UFLAG1 | UFLAG2 | UFLAG3 | UFLAG4) 615 == "0xf [UFLAG1 UFLAG3 0xa]"); 616 617 SELF_CHECK (to_string_flags (0) 618 == "0x0 []"); 619 SELF_CHECK (to_string_flags (FLAG1) 620 == "0x1 [FLAG1]"); 621 SELF_CHECK (to_string_flags (FLAG1 | FLAG3) 622 == "0x5 [FLAG1 FLAG3]"); 623 SELF_CHECK (to_string_flags (FLAG1 | FLAG2 | FLAG3) 624 == "0x7 [FLAG1 FLAG3 0x2]"); 625 SELF_CHECK (to_string_flags (FLAG2) 626 == "0x2 [0x2]"); 627 SELF_CHECK (to_string_flags (FLAG1 | FLAG2 | FLAG3 | FLAG4) 628 == "0xf [FLAG1 FLAG3 0xa]"); 629 } 630 } 631 632 } /* namespace enum_flags_tests */ 633 } /* namespace selftests */ 634 635 void _initialize_enum_flags_selftests (); 636 637 void 638 _initialize_enum_flags_selftests () 639 { 640 selftests::register_test ("enum-flags", 641 selftests::enum_flags_tests::self_test); 642 } 643