diff options
| author | Josef Schlehofer | 2026-06-07 20:47:09 +0000 |
|---|---|---|
| committer | Robert Marko | 2026-06-08 08:22:54 +0000 |
| commit | 81b3344b60f50d43ddf007376ec275376646822f (patch) | |
| tree | ba9968d552ce4f7e07fda49a5647a8ed89c4698d | |
| parent | 4508dd6014bc948f1ceb1dd705202527ac508e3d (diff) | |
toolchain: gdb: backport patch to fix build with Clang 21
Backport a patch [1] to fix a compile error in enum-flags.h [2].
[1] https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=commit;h=4a0b2cb7210
[2] https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=293168
Fixes: https://github.com/openwrt/openwrt/issues/23122
Signed-off-by: Josef Schlehofer <pepe.schlehofer@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/23701
Signed-off-by: Robert Marko <robimarko@gmail.com>
| -rw-r--r-- | toolchain/gdb/patches/140-fix-clang21-enum-constexpr-conversion.patch | 242 |
1 files changed, 242 insertions, 0 deletions
diff --git a/toolchain/gdb/patches/140-fix-clang21-enum-constexpr-conversion.patch b/toolchain/gdb/patches/140-fix-clang21-enum-constexpr-conversion.patch new file mode 100644 index 0000000000..d6e7c0015d --- /dev/null +++ b/toolchain/gdb/patches/140-fix-clang21-enum-constexpr-conversion.patch @@ -0,0 +1,242 @@ +From 4a0b2cb7210c65c8c9f4a56345749bb7294fbfbf Mon Sep 17 00:00:00 2001 +From: Carlos Galvez <carlosgalvezp@gmail.com> +Date: Sat, 21 Dec 2024 21:25:40 +0000 +Subject: [PATCH] Fix -Wenum-constexpr-conversion in enum-flags.h + +This fixes PR 31331: +https://sourceware.org/bugzilla/show_bug.cgi?id=31331 + +Currently, enum-flags.h implements enum_underlying_type by casting +integer -1 to the enum type, checking if it is less than 0, and +choosing signed/unsigned underlying type based on that. + +This is undefined behavior in C++ if the integer falls outside the range +of the enum, which Clang 21 detects and flags as a constexpr conversion error. + +Fix it by using std::underlying_type instead, which is standard and does +not suffer from this issue. + +Signed-off-by: Carlos Galvez <carlosgalvezp@gmail.com> +--- + gdb/unittests/enum-flags-selftests.c | 27 --------------------------- + gdbsupport/enum-flags.h | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------- + include/diagnostics.h | 9 --------- + 3 files changed, 63 insertions(+), 56 deletions(-) + +--- a/gdb/unittests/enum-flags-selftests.c ++++ b/gdb/unittests/enum-flags-selftests.c +@@ -233,33 +233,6 @@ CHECK_VALID (true, EF, true ? RE () : EF ()) + 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 ()) +-CHECK_VALID (true, int, true ? EF () : RE2 ()) +-CHECK_VALID (true, int, true ? RE2 () : EF ()) +- +-/* Same, but with an unsigned enum. */ +- +-typedef unsigned int uns; +- +-CHECK_VALID (true, uns, true ? EF () : UEF ()) +-CHECK_VALID (true, uns, true ? UEF () : EF ()) +-CHECK_VALID (true, uns, true ? EF () : URE ()) +-CHECK_VALID (true, uns, true ? URE () : EF ()) +- + /* 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 +--- a/gdbsupport/enum-flags.h ++++ b/gdbsupport/enum-flags.h +@@ -78,30 +78,6 @@ + namespace. The compiler finds the corresponding + is_enum_flags_enum_type function via ADL. */ + +-/* Note that std::underlying_type<enum_type> is not what we want here, +- since that returns unsigned int even when the enum decays to signed +- int. */ +-template<int size, bool sign> class integer_for_size { typedef void type; }; +-template<> struct integer_for_size<1, 0> { typedef uint8_t type; }; +-template<> struct integer_for_size<2, 0> { typedef uint16_t type; }; +-template<> struct integer_for_size<4, 0> { typedef uint32_t type; }; +-template<> struct integer_for_size<8, 0> { typedef uint64_t type; }; +-template<> struct integer_for_size<1, 1> { typedef int8_t type; }; +-template<> struct integer_for_size<2, 1> { typedef int16_t type; }; +-template<> struct integer_for_size<4, 1> { typedef int32_t type; }; +-template<> struct integer_for_size<8, 1> { typedef int64_t type; }; +- +-template<typename T> +-struct enum_underlying_type +-{ +- DIAGNOSTIC_PUSH +- DIAGNOSTIC_IGNORE_ENUM_CONSTEXPR_CONVERSION +- typedef typename +- integer_for_size<sizeof (T), static_cast<bool>(T (-1) < T (0))>::type +- type; +- DIAGNOSTIC_POP +-}; +- + namespace enum_flags_detail + { + +@@ -119,11 +95,62 @@ + /* gdb::Requires trait helpers. */ + template <typename enum_type> + using EnumIsUnsigned +- = std::is_unsigned<typename enum_underlying_type<enum_type>::type>; ++ = std::is_unsigned<typename std::underlying_type<enum_type>::type>; ++ ++/* Helper to detect whether an enum has a fixed underlying type. This can be ++ achieved by using a scoped enum (in which case the type is "int") or ++ an explicit underlying type. C-style enums that are unscoped or do not ++ have an explicit underlying type have an implementation-defined underlying ++ type. ++ ++ https://timsong-cpp.github.io/cppwp/n4659/dcl.enum#5 ++ ++ We need this trait in order to ensure that operator~ below does NOT ++ operate on old-style enums. This is because we apply operator~ on ++ the value and then cast the result to the enum_type. This is however ++ Undefined Behavior if the result does not fit in the range of possible ++ values for the enum. For enums with fixed underlying type, the entire ++ range of the integer is available. However, for old-style enums, the range ++ is only the smallest bit-field that can hold all the values of the ++ enumeration, typically much smaller than the underlying integer: ++ ++ https://timsong-cpp.github.io/cppwp/n4659/expr.static.cast#10 ++ https://timsong-cpp.github.io/cppwp/n4659/dcl.enum#8 ++ ++ To implement this, we leverage the fact that, since C++17, enums with ++ fixed underlying type can be list-initialized from an integer: ++ https://timsong-cpp.github.io/cppwp/n4659/dcl.init.list#3.7 ++ ++ Old-style enums cannot be initialized like that, leading to ill-formed ++ code. ++ ++ We then use this together with SFINAE to create the desired trait. ++ ++*/ ++template <typename enum_type, typename = void> ++struct EnumHasFixedUnderlyingType : std::false_type ++{ ++ static_assert(std::is_enum<enum_type>::value); ++}; ++ ++/* Specialization that is active only if enum_type can be ++ list-initialized from an integer (0). Only enums with fixed ++ underlying type satisfy this property in C++17. */ + template <typename enum_type> +-using EnumIsSigned +- = std::is_signed<typename enum_underlying_type<enum_type>::type>; ++struct EnumHasFixedUnderlyingType<enum_type, std::void_t<decltype(enum_type{0})>> : std::true_type ++{ ++ static_assert(std::is_enum<enum_type>::value); ++}; + ++template <typename enum_type> ++using EnumIsSafeForBitwiseComplement = std::conjunction< ++ EnumIsUnsigned<enum_type>, ++ EnumHasFixedUnderlyingType<enum_type> ++>; ++ ++template <typename enum_type> ++using EnumIsUnsafeForBitwiseComplement = std::negation<EnumIsSafeForBitwiseComplement<enum_type>>; ++ + } + + template <typename E> +@@ -131,7 +158,7 @@ + { + public: + typedef E enum_type; +- typedef typename enum_underlying_type<enum_type>::type underlying_type; ++ typedef typename std::underlying_type<enum_type>::type underlying_type; + + /* For to_string. Maps one enumerator of E to a string. */ + struct string_mapping +@@ -394,33 +421,41 @@ + template <typename enum_type, + typename = is_enum_flags_enum_type_t<enum_type>, + typename +- = gdb::Requires<enum_flags_detail::EnumIsUnsigned<enum_type>>> ++ = gdb::Requires<enum_flags_detail::EnumIsSafeForBitwiseComplement<enum_type>>> + constexpr enum_type + operator~ (enum_type e) + { + using underlying = typename enum_flags<enum_type>::underlying_type; +- return (enum_type) ~underlying (e); ++ /* Cast to ULONGEST first, to prevent integer promotions from enums ++ with fixed underlying type std::uint8_t or std::uint16_t to ++ signed int. This ensures we apply the bitwise complement on an ++ unsigned type. */ ++ return (enum_type)(underlying) ~ULONGEST (e); + } + + template <typename enum_type, + typename = is_enum_flags_enum_type_t<enum_type>, +- typename = gdb::Requires<enum_flags_detail::EnumIsSigned<enum_type>>> ++ typename = gdb::Requires<enum_flags_detail::EnumIsUnsafeForBitwiseComplement<enum_type>>> + constexpr void operator~ (enum_type e) = delete; + + template <typename enum_type, + typename = is_enum_flags_enum_type_t<enum_type>, + typename +- = gdb::Requires<enum_flags_detail::EnumIsUnsigned<enum_type>>> ++ = gdb::Requires<enum_flags_detail::EnumIsSafeForBitwiseComplement<enum_type>>> + constexpr enum_flags<enum_type> + operator~ (enum_flags<enum_type> e) + { + using underlying = typename enum_flags<enum_type>::underlying_type; +- return (enum_type) ~underlying (e); ++ /* Cast to ULONGEST first, to prevent integer promotions from enums ++ with fixed underlying type std::uint8_t or std::uint16_t to ++ signed int. This ensures we apply the bitwise complement on an ++ unsigned type. */ ++ return (enum_type)(underlying) ~ULONGEST (e); + } + + template <typename enum_type, + typename = is_enum_flags_enum_type_t<enum_type>, +- typename = gdb::Requires<enum_flags_detail::EnumIsSigned<enum_type>>> ++ typename = gdb::Requires<enum_flags_detail::EnumIsUnsafeForBitwiseComplement<enum_type>>> + constexpr void operator~ (enum_flags<enum_type> e) = delete; + + /* Delete operator<< and operator>>. */ +--- a/include/diagnostics.h ++++ b/include/diagnostics.h +@@ -76,11 +76,6 @@ + # define DIAGNOSTIC_ERROR_SWITCH \ + DIAGNOSTIC_ERROR ("-Wswitch") + +-# if __has_warning ("-Wenum-constexpr-conversion") +-# define DIAGNOSTIC_IGNORE_ENUM_CONSTEXPR_CONVERSION \ +- DIAGNOSTIC_IGNORE ("-Wenum-constexpr-conversion") +-# endif +- + #elif defined (__GNUC__) /* GCC */ + + # define DIAGNOSTIC_IGNORE_DEPRECATED_DECLARATIONS \ +@@ -157,10 +152,6 @@ + + #ifndef DIAGNOSTIC_ERROR_SWITCH + # define DIAGNOSTIC_ERROR_SWITCH +-#endif +- +-#ifndef DIAGNOSTIC_IGNORE_ENUM_CONSTEXPR_CONVERSION +-# define DIAGNOSTIC_IGNORE_ENUM_CONSTEXPR_CONVERSION + #endif + + #endif /* DIAGNOSTICS_H */ |