https://gcc.gnu.org/g:6570fa6f2612a4e4ddd2fcfc119369a1a48656e4

commit r15-8044-g6570fa6f2612a4e4ddd2fcfc119369a1a48656e4
Author: Patrick Palka <ppa...@redhat.com>
Date:   Thu Mar 13 19:55:00 2025 -0400

    libstdc++: Work around C++20 tuple<tuple<any>> constraint recursion 
[PR116440]
    
    The type tuple<tuple<any>> 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 <jwak...@redhat.com>

Diff:
---
 libstdc++-v3/include/std/tuple                | 14 +++++++------
 libstdc++-v3/testsuite/20_util/tuple/116440.C | 29 +++++++++++++++++++++++++++
 2 files changed, 37 insertions(+), 6 deletions(-)

diff --git a/libstdc++-v3/include/std/tuple b/libstdc++-v3/include/std/tuple
index 34d790fd6f59..d3deb7bc1241 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<const _Elements&...>())
-      tuple(const _Elements&... __elements)
-      noexcept(__nothrow_constructible<const _Elements&...>())
-      requires (__constructible<const _Elements&...>())
-      : _Inherited(__elements...)
-      { }
+      // Defined as a template to work around PR libstdc++/116440.
+      template<typename = void>
+       constexpr explicit(!__convertible<const _Elements&...>())
+       tuple(const _Elements&... __elements)
+       noexcept(__nothrow_constructible<const _Elements&...>())
+       requires (__constructible<const _Elements&...>())
+       : _Inherited(__elements...)
+       { }
 
       template<typename... _UTypes>
        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 000000000000..12259134d251
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/tuple/116440.C
@@ -0,0 +1,29 @@
+// PR libstdc++/116440 - std::tuple<std::tuple<std::any>> does not compile
+// { dg-do compile { target c++17 } }
+
+#include <any>
+#include <tuple>
+#include <type_traits>
+
+template <typename T>
+using TupleTuple = std::tuple<std::tuple<T>>;
+
+struct EmbedAny {
+    std::any content;
+};
+
+static_assert(std::is_copy_constructible<TupleTuple<EmbedAny>>::value);
+static_assert(std::is_move_constructible<TupleTuple<EmbedAny>>::value);
+
+static_assert(std::is_copy_constructible<TupleTuple<std::any>>::value);
+static_assert(std::is_move_constructible<TupleTuple<std::any>>::value);
+
+static_assert(std::is_constructible_v<std::any, TupleTuple<std::any>>);
+
+struct EmbedAnyWithZeroSizeArray {
+    void* pad[0];
+    std::any content;
+};
+
+static_assert(std::is_copy_constructible<TupleTuple<EmbedAnyWithZeroSizeArray>>::value);
+static_assert(std::is_move_constructible<TupleTuple<EmbedAnyWithZeroSizeArray>>::value);

Reply via email to