123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596 |
- /* Self tests for enum-flags for GDB, the GNU debugger.
- Copyright (C) 2016-2022 Free Software Foundation, Inc.
- This file is part of GDB.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
- #include "defs.h"
- #include "gdbsupport/enum-flags.h"
- #include "gdbsupport/valid-expr.h"
- #include "gdbsupport/selftest.h"
- namespace selftests {
- namespace enum_flags_tests {
- /* The (real) enum types used in CHECK_VALID. Their names match the
- template parameter names of the templates defined by CHECK_VALID to
- make it simpler to use. They could be named differently. */
- /* A "real enum". */
- enum RE
- {
- RE_FLAG1 = 1 << 1,
- RE_FLAG2 = 1 << 2,
- };
- /* Another "real enum". */
- enum RE2
- {
- RE2_FLAG1 = 1 << 1,
- RE2_FLAG2 = 1 << 2,
- };
- /* An unsigned "real enum". */
- enum URE : unsigned
- {
- URE_FLAG1 = 1 << 1,
- URE_FLAG2 = 1 << 2,
- URE_FLAG3 = 0xffffffff,
- };
- /* A non-flags enum. */
- enum NF
- {
- NF_FLAG1 = 1 << 1,
- NF_FLAG2 = 1 << 2,
- };
- /* The corresponding "enum flags" types. */
- DEF_ENUM_FLAGS_TYPE (RE, EF);
- DEF_ENUM_FLAGS_TYPE (RE2, EF2);
- DEF_ENUM_FLAGS_TYPE (URE, UEF);
- #if HAVE_IS_TRIVIALLY_COPYABLE
- /* So that std::vectors of types that have enum_flags fields can
- reallocate efficiently memcpy. */
- gdb_static_assert (std::is_trivially_copyable<EF>::value);
- #endif
- /* A couple globals used as lvalues in the CHECK_VALID expressions
- below. Their names (and types) match the uppercase type names
- exposed by CHECK_VALID just to make the expressions easier to
- follow. */
- static RE re ATTRIBUTE_UNUSED;
- static EF ef ATTRIBUTE_UNUSED;
- /* First, compile-time tests that:
- - make sure that incorrect operations with mismatching enum types
- are caught at compile time.
- - make sure that the same operations but involving the right enum
- types do compile and that they return the correct type.
- */
- #define CHECK_VALID(VALID, EXPR_TYPE, EXPR) \
- CHECK_VALID_EXPR_6 (EF, RE, EF2, RE2, UEF, URE, VALID, EXPR_TYPE, EXPR)
- typedef std::underlying_type<RE>::type und;
- /* Test construction / conversion from/to different types. */
- /* RE/EF -> underlying (explicit) */
- CHECK_VALID (true, und, und (RE ()))
- CHECK_VALID (true, und, und (EF ()))
- /* RE/EF -> int (explicit) */
- CHECK_VALID (true, int, int (RE ()))
- CHECK_VALID (true, int, int (EF ()))
- /* other -> RE */
- /* You can construct a raw enum value from an int explicitly to punch
- a hole in the type system if need to. */
- CHECK_VALID (true, RE, RE (1))
- CHECK_VALID (true, RE, RE (RE2 ()))
- CHECK_VALID (false, void, RE (EF2 ()))
- CHECK_VALID (true, RE, RE (RE ()))
- CHECK_VALID (false, void, RE (EF ()))
- /* other -> EF. */
- /* As expected, enum-flags is a stronger type than the backing raw
- enum. Unlike with raw enums, you can't construct an enum flags
- from an integer nor from an unrelated enum type explicitly. Add an
- intermediate conversion via the raw enum if you really need it. */
- CHECK_VALID (false, void, EF (1))
- CHECK_VALID (false, void, EF (1u))
- CHECK_VALID (false, void, EF (RE2 ()))
- CHECK_VALID (false, void, EF (EF2 ()))
- CHECK_VALID (true, EF, EF (RE ()))
- CHECK_VALID (true, EF, EF (EF ()))
- /* Test operators. */
- /* operator OP (raw_enum, int) */
- CHECK_VALID (false, void, RE () | 1)
- CHECK_VALID (false, void, RE () & 1)
- CHECK_VALID (false, void, RE () ^ 1)
- /* operator OP (int, raw_enum) */
- CHECK_VALID (false, void, 1 | RE ())
- CHECK_VALID (false, void, 1 & RE ())
- CHECK_VALID (false, void, 1 ^ RE ())
- /* operator OP (enum_flags, int) */
- CHECK_VALID (false, void, EF () | 1)
- CHECK_VALID (false, void, EF () & 1)
- CHECK_VALID (false, void, EF () ^ 1)
- /* operator OP (int, enum_flags) */
- CHECK_VALID (false, void, 1 | EF ())
- CHECK_VALID (false, void, 1 & EF ())
- CHECK_VALID (false, void, 1 ^ EF ())
- /* operator OP (raw_enum, raw_enum) */
- CHECK_VALID (false, void, RE () | RE2 ())
- CHECK_VALID (false, void, RE () & RE2 ())
- CHECK_VALID (false, void, RE () ^ RE2 ())
- CHECK_VALID (true, RE, RE () | RE ())
- CHECK_VALID (true, RE, RE () & RE ())
- CHECK_VALID (true, RE, RE () ^ RE ())
- /* operator OP (enum_flags, raw_enum) */
- CHECK_VALID (false, void, EF () | RE2 ())
- CHECK_VALID (false, void, EF () & RE2 ())
- CHECK_VALID (false, void, EF () ^ RE2 ())
- CHECK_VALID (true, EF, EF () | RE ())
- CHECK_VALID (true, EF, EF () & RE ())
- CHECK_VALID (true, EF, EF () ^ RE ())
- /* operator OP= (raw_enum, raw_enum), rvalue ref on the lhs. */
- CHECK_VALID (false, void, RE () |= RE2 ())
- CHECK_VALID (false, void, RE () &= RE2 ())
- CHECK_VALID (false, void, RE () ^= RE2 ())
- CHECK_VALID (false, void, RE () |= RE ())
- CHECK_VALID (false, void, RE () &= RE ())
- CHECK_VALID (false, void, RE () ^= RE ())
- /* operator OP= (raw_enum, raw_enum), lvalue ref on the lhs. */
- CHECK_VALID (false, void, re |= RE2 ())
- CHECK_VALID (false, void, re &= RE2 ())
- CHECK_VALID (false, void, re ^= RE2 ())
- CHECK_VALID (true, RE&, re |= RE ())
- CHECK_VALID (true, RE&, re &= RE ())
- CHECK_VALID (true, RE&, re ^= RE ())
- /* operator OP= (enum_flags, raw_enum), rvalue ref on the lhs. */
- CHECK_VALID (false, void, EF () |= RE2 ())
- CHECK_VALID (false, void, EF () &= RE2 ())
- CHECK_VALID (false, void, EF () ^= RE2 ())
- CHECK_VALID (false, void, EF () |= RE ())
- CHECK_VALID (false, void, EF () &= RE ())
- CHECK_VALID (false, void, EF () ^= RE ())
- /* operator OP= (enum_flags, raw_enum), lvalue ref on the lhs. */
- CHECK_VALID (false, void, ef |= RE2 ())
- CHECK_VALID (false, void, ef &= RE2 ())
- CHECK_VALID (false, void, ef ^= RE2 ())
- CHECK_VALID (true, EF&, ef |= EF ())
- CHECK_VALID (true, EF&, ef &= EF ())
- CHECK_VALID (true, EF&, ef ^= EF ())
- /* operator OP= (enum_flags, enum_flags), rvalue ref on the lhs. */
- CHECK_VALID (false, void, EF () |= EF2 ())
- CHECK_VALID (false, void, EF () &= EF2 ())
- CHECK_VALID (false, void, EF () ^= EF2 ())
- CHECK_VALID (false, void, EF () |= EF ())
- CHECK_VALID (false, void, EF () &= EF ())
- CHECK_VALID (false, void, EF () ^= EF ())
- /* operator OP= (enum_flags, enum_flags), lvalue ref on the lhs. */
- CHECK_VALID (false, void, ef |= EF2 ())
- CHECK_VALID (false, void, ef &= EF2 ())
- CHECK_VALID (false, void, ef ^= EF2 ())
- CHECK_VALID (true, EF&, ef |= EF ())
- CHECK_VALID (true, EF&, ef &= EF ())
- CHECK_VALID (true, EF&, ef ^= EF ())
- /* operator~ (raw_enum) */
- CHECK_VALID (false, void, ~RE ())
- CHECK_VALID (true, URE, ~URE ())
- /* operator~ (enum_flags) */
- CHECK_VALID (false, void, ~EF ())
- CHECK_VALID (true, UEF, ~UEF ())
- /* Check ternary operator. This exercises implicit conversions. */
- CHECK_VALID (true, EF, true ? EF () : RE ())
- CHECK_VALID (true, EF, true ? RE () : EF ())
- /* These are valid, but it's not a big deal since you won't be able to
- assign the resulting integer to an enum or an enum_flags without a
- cast.
- The latter two tests are disabled on older GCCs because they
- incorrectly fail with gcc 4.8 and 4.9 at least. Running the test
- outside a SFINAE context shows:
- invalid user-defined conversion from ‘EF’ to ‘RE2’
- They've been confirmed to compile/pass with gcc 5.3, gcc 7.1 and
- clang 3.7. */
- CHECK_VALID (true, int, true ? EF () : EF2 ())
- CHECK_VALID (true, int, true ? EF2 () : EF ())
- #if GCC_VERSION >= 5003 || defined __clang__
- CHECK_VALID (true, int, true ? EF () : RE2 ())
- CHECK_VALID (true, int, true ? RE2 () : EF ())
- #endif
- /* Same, but with an unsigned enum. */
- typedef unsigned int uns;
- CHECK_VALID (true, uns, true ? EF () : UEF ())
- CHECK_VALID (true, uns, true ? UEF () : EF ())
- #if GCC_VERSION >= 5003 || defined __clang__
- CHECK_VALID (true, uns, true ? EF () : URE ())
- CHECK_VALID (true, uns, true ? URE () : EF ())
- #endif
- /* Unfortunately this can't work due to the way C++ computes the
- return type of the ternary conditional operator. int isn't
- implicitly convertible to the raw enum type, so the type of the
- expression is int. And then int is not implicitly convertible to
- enum_flags.
- GCC 4.8 fails to compile this test with:
- error: operands to ?: have different types ‘enum_flags<RE>’ and ‘int’
- Confirmed to work with gcc 4.9, 5.3 and clang 3.7.
- */
- #if GCC_VERSION >= 4009 || defined __clang__
- CHECK_VALID (false, void, true ? EF () : 0)
- CHECK_VALID (false, void, true ? 0 : EF ())
- #endif
- /* Check that the ++/--/<</>>/<<=/>>= operators are deleted. */
- CHECK_VALID (false, void, RE ()++)
- CHECK_VALID (false, void, ++RE ())
- CHECK_VALID (false, void, --RE ())
- CHECK_VALID (false, void, RE ()--)
- CHECK_VALID (false, void, RE () << 1)
- CHECK_VALID (false, void, RE () >> 1)
- CHECK_VALID (false, void, EF () << 1)
- CHECK_VALID (false, void, EF () >> 1)
- CHECK_VALID (false, void, RE () <<= 1)
- CHECK_VALID (false, void, RE () >>= 1)
- CHECK_VALID (false, void, EF () <<= 1)
- CHECK_VALID (false, void, EF () >>= 1)
- /* Test comparison operators. */
- CHECK_VALID (false, void, EF () == EF2 ())
- CHECK_VALID (false, void, EF () == RE2 ())
- CHECK_VALID (false, void, RE () == EF2 ())
- CHECK_VALID (true, bool, EF (RE (1)) == EF (RE (1)))
- CHECK_VALID (true, bool, EF (RE (1)) == RE (1))
- CHECK_VALID (true, bool, RE (1) == EF (RE (1)))
- CHECK_VALID (false, void, EF () != EF2 ())
- CHECK_VALID (false, void, EF () != RE2 ())
- CHECK_VALID (false, void, RE () != EF2 ())
- /* Disable -Wenum-compare due to:
- Clang:
- "error: comparison of two values with different enumeration types
- [-Werror,-Wenum-compare]"
- GCC:
- "error: comparison between ‘enum selftests::enum_flags_tests::RE’
- and ‘enum selftests::enum_flags_tests::RE2’
- [-Werror=enum-compare]"
- Not a big deal since misuses like these in GDB will be caught by
- -Werror anyway. This check is here mainly for completeness. */
- #if defined __GNUC__
- # pragma GCC diagnostic push
- # pragma GCC diagnostic ignored "-Wenum-compare"
- #endif
- CHECK_VALID (true, bool, RE () == RE2 ())
- CHECK_VALID (true, bool, RE () != RE2 ())
- #if defined __GNUC__
- # pragma GCC diagnostic pop
- #endif
- CHECK_VALID (true, bool, EF (RE (1)) != EF (RE (2)))
- CHECK_VALID (true, bool, EF (RE (1)) != RE (2))
- CHECK_VALID (true, bool, RE (1) != EF (RE (2)))
- CHECK_VALID (true, bool, EF () == 0)
- /* Check we didn't disable/delete comparison between non-flags enums
- and unrelated types by mistake. */
- CHECK_VALID (true, bool, NF (1) == NF (1))
- CHECK_VALID (true, bool, NF (1) == int (1))
- CHECK_VALID (true, bool, NF (1) == char (1))
- /* -------------------------------------------------------------------- */
- /* Follows misc tests that exercise the API. Some are compile time,
- when possible, others are run time. */
- enum test_flag
- {
- FLAG1 = 1 << 1,
- FLAG2 = 1 << 2,
- FLAG3 = 1 << 3,
- };
- enum test_uflag : unsigned
- {
- UFLAG1 = 1 << 1,
- UFLAG2 = 1 << 2,
- UFLAG3 = 1 << 3,
- };
- DEF_ENUM_FLAGS_TYPE (test_flag, test_flags);
- DEF_ENUM_FLAGS_TYPE (test_uflag, test_uflags);
- static void
- self_test ()
- {
- /* Check that default construction works. */
- {
- constexpr test_flags f;
- gdb_static_assert (f == 0);
- }
- /* Check that assignment from zero works. */
- {
- test_flags f (FLAG1);
- SELF_CHECK (f == FLAG1);
- f = 0;
- SELF_CHECK (f == 0);
- }
- /* Check that construction from zero works. */
- {
- constexpr test_flags zero1 = 0;
- constexpr test_flags zero2 (0);
- constexpr test_flags zero3 {0};
- constexpr test_flags zero4 = {0};
- gdb_static_assert (zero1 == 0);
- gdb_static_assert (zero2 == 0);
- gdb_static_assert (zero3 == 0);
- gdb_static_assert (zero4 == 0);
- }
- /* Check construction from enum value. */
- {
- gdb_static_assert (test_flags (FLAG1) == FLAG1);
- gdb_static_assert (test_flags (FLAG2) != FLAG1);
- }
- /* Check copy/assignment. */
- {
- constexpr test_flags src = FLAG1;
- constexpr test_flags f1 = src;
- constexpr test_flags f2 (src);
- constexpr test_flags f3 {src};
- constexpr test_flags f4 = {src};
- gdb_static_assert (f1 == FLAG1);
- gdb_static_assert (f2 == FLAG1);
- gdb_static_assert (f3 == FLAG1);
- gdb_static_assert (f4 == FLAG1);
- }
- /* Check moving. */
- {
- test_flags src = FLAG1;
- test_flags dst = 0;
- dst = std::move (src);
- SELF_CHECK (dst == FLAG1);
- }
- /* Check construction from an 'or' of multiple bits. For this to
- work, operator| must be overridden to return an enum type. The
- builtin version would return int instead and then the conversion
- to test_flags would fail. */
- {
- constexpr test_flags f = FLAG1 | FLAG2;
- gdb_static_assert (f == (FLAG1 | FLAG2));
- }
- /* Similarly, check that "FLAG1 | FLAG2" on the rhs of an assignment
- operator works. */
- {
- test_flags f = 0;
- f |= FLAG1 | FLAG2;
- SELF_CHECK (f == (FLAG1 | FLAG2));
- f &= FLAG1 | FLAG2;
- SELF_CHECK (f == (FLAG1 | FLAG2));
- f ^= FLAG1 | FLAG2;
- SELF_CHECK (f == 0);
- }
- /* Check explicit conversion to int works. */
- {
- constexpr int some_bits (FLAG1 | FLAG2);
- /* And comparison with int works too. */
- gdb_static_assert (some_bits == (FLAG1 | FLAG2));
- gdb_static_assert (some_bits == test_flags (FLAG1 | FLAG2));
- }
- /* Check operator| and operator|=. Particularly interesting is
- making sure that putting the enum value on the lhs side of the
- expression works (FLAG | f). */
- {
- test_flags f = FLAG1;
- f |= FLAG2;
- SELF_CHECK (f == (FLAG1 | FLAG2));
- }
- {
- test_flags f = FLAG1;
- f = f | FLAG2;
- SELF_CHECK (f == (FLAG1 | FLAG2));
- }
- {
- test_flags f = FLAG1;
- f = FLAG2 | f;
- SELF_CHECK (f == (FLAG1 | FLAG2));
- }
- /* Check the &/&= operators. */
- {
- test_flags f = FLAG1 & FLAG2;
- SELF_CHECK (f == 0);
- f = FLAG1 | FLAG2;
- f &= FLAG2;
- SELF_CHECK (f == FLAG2);
- f = FLAG1 | FLAG2;
- f = f & FLAG2;
- SELF_CHECK (f == FLAG2);
- f = FLAG1 | FLAG2;
- f = FLAG2 & f;
- SELF_CHECK (f == FLAG2);
- }
- /* Check the ^/^= operators. */
- {
- constexpr test_flags f = FLAG1 ^ FLAG2;
- gdb_static_assert (f == (FLAG1 ^ FLAG2));
- }
- {
- test_flags f = FLAG1 ^ FLAG2;
- f ^= FLAG3;
- SELF_CHECK (f == (FLAG1 | FLAG2 | FLAG3));
- f = f ^ FLAG3;
- SELF_CHECK (f == (FLAG1 | FLAG2));
- f = FLAG3 ^ f;
- SELF_CHECK (f == (FLAG1 | FLAG2 | FLAG3));
- }
- /* Check operator~. Note this only compiles with unsigned
- flags. */
- {
- constexpr test_uflags f1 = ~UFLAG1;
- constexpr test_uflags f2 = ~f1;
- gdb_static_assert (f2 == UFLAG1);
- }
- /* Check the ternary operator. */
- {
- /* raw enum, raw enum */
- constexpr test_flags f1 = true ? FLAG1 : FLAG2;
- gdb_static_assert (f1 == FLAG1);
- constexpr test_flags f2 = false ? FLAG1 : FLAG2;
- gdb_static_assert (f2 == FLAG2);
- }
- {
- /* enum flags, raw enum */
- constexpr test_flags src = FLAG1;
- constexpr test_flags f1 = true ? src : FLAG2;
- gdb_static_assert (f1 == FLAG1);
- constexpr test_flags f2 = false ? src : FLAG2;
- gdb_static_assert (f2 == FLAG2);
- }
- {
- /* enum flags, enum flags */
- constexpr test_flags src1 = FLAG1;
- constexpr test_flags src2 = FLAG2;
- constexpr test_flags f1 = true ? src1 : src2;
- gdb_static_assert (f1 == src1);
- constexpr test_flags f2 = false ? src1 : src2;
- gdb_static_assert (f2 == src2);
- }
- /* Check that we can use flags in switch expressions (requires
- unambiguous conversion to integer). Also check that we can use
- operator| in switch cases, where only constants are allowed.
- This should work because operator| is constexpr. */
- {
- test_flags f = FLAG1 | FLAG2;
- bool ok = false;
- switch (f)
- {
- case FLAG1:
- break;
- case FLAG2:
- break;
- case FLAG1 | FLAG2:
- ok = true;
- break;
- }
- SELF_CHECK (ok);
- }
- }
- } /* namespace enum_flags_tests */
- } /* namespace selftests */
- void _initialize_enum_flags_selftests ();
- void
- _initialize_enum_flags_selftests ()
- {
- selftests::register_test ("enum-flags",
- selftests::enum_flags_tests::self_test);
- }
|