From 6570fa6f2612a4e4ddd2fcfc119369a1a48656e4 Mon Sep 17 00:00:00 2001 From: Patrick Palka Date: Thu, 13 Mar 2025 19:55:00 -0400 Subject: [PATCH] libstdc++: Work around C++20 tuple> constraint recursion [PR116440] The type tuple> is clearly copy/move constructible, but for reasons that are not yet completely understood checking this triggers constraint recursion with our C++20 tuple implementation (but not the C++17 implementation). It turns out this recursion stems from considering the non-template tuple(const _Elements&) constructor during the copy/move constructibility check. Considering this constructor is ultimately redundant, since the defaulted copy/move constructors are better matches. GCC has a non-standard "perfect candidate" optimization[1] that causes overload resolution to shortcut considering template candidates if we find a (non-template) perfect candidate. So to work around this issue (and as a general compile-time optimization) this patch turns the problematic constructor into a template so that GCC doesn't consider it when checking for copy/move constructibility of this tuple type. Changing the template-ness of a constructor can affect overload resolution (since template-ness is a tiebreaker) so there's a risk this change could e.g. introduce overload resolution ambiguities. But the original C++17 implementation has long defined this constructor as a template (in order to constrain it etc), so doing the same thing in the C++20 mode should naturally be quite safe. The testcase still fails with Clang (in C++20 mode) since it doesn't implement said optimization. [1]: See r11-7287-g187d0d5871b1fa and https://isocpp.org/files/papers/P3606R0.html PR libstdc++/116440 libstdc++-v3/ChangeLog: * include/std/tuple (tuple::tuple(const _Elements&...)) [C++20]: Turn into a template. * testsuite/20_util/tuple/116440.C: New test. Reviewed-by: Jonathan Wakely --- libstdc++-v3/include/std/tuple | 14 +++++---- libstdc++-v3/testsuite/20_util/tuple/116440.C | 29 +++++++++++++++++++ 2 files changed, 37 insertions(+), 6 deletions(-) create mode 100644 libstdc++-v3/testsuite/20_util/tuple/116440.C diff --git a/libstdc++-v3/include/std/tuple b/libstdc++-v3/include/std/tuple index 34d790fd6f5..d3deb7bc124 100644 --- a/libstdc++-v3/include/std/tuple +++ b/libstdc++-v3/include/std/tuple @@ -966,12 +966,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION : _Inherited() { } - constexpr explicit(!__convertible()) - tuple(const _Elements&... __elements) - noexcept(__nothrow_constructible()) - requires (__constructible()) - : _Inherited(__elements...) - { } + // Defined as a template to work around PR libstdc++/116440. + template + constexpr explicit(!__convertible()) + tuple(const _Elements&... __elements) + noexcept(__nothrow_constructible()) + requires (__constructible()) + : _Inherited(__elements...) + { } template requires (__disambiguating_constraint<_UTypes...>()) diff --git a/libstdc++-v3/testsuite/20_util/tuple/116440.C b/libstdc++-v3/testsuite/20_util/tuple/116440.C new file mode 100644 index 00000000000..12259134d25 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/tuple/116440.C @@ -0,0 +1,29 @@ +// PR libstdc++/116440 - std::tuple> does not compile +// { dg-do compile { target c++17 } } + +#include +#include +#include + +template +using TupleTuple = std::tuple>; + +struct EmbedAny { + std::any content; +}; + +static_assert(std::is_copy_constructible>::value); +static_assert(std::is_move_constructible>::value); + +static_assert(std::is_copy_constructible>::value); +static_assert(std::is_move_constructible>::value); + +static_assert(std::is_constructible_v>); + +struct EmbedAnyWithZeroSizeArray { + void* pad[0]; + std::any content; +}; + +static_assert(std::is_copy_constructible>::value); +static_assert(std::is_move_constructible>::value);