Tested on x86_64-pc-linux-gnu, does this look OK for trunk, and 14 after
a while?
-- >8 --
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 (and not the
C++17 implementation).
It turns out this recursion stems from considering the non-template
tuple(const _Elements&) constructor when checking for copy/move
constructibility. Checking this constructor is of course 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 tuple
bug (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.
Changing the template-ness of a constructor can affect the outcome of
overload resolution (since template-ness is a tiebreaker) so there's a
risk this change could cause overload resolution ambiguities. The tuple
constructor set doesn't seem particularly reliant on this tiebreaker
though.
The testcase still fails with Clang (in C++20 mode) since it doesn't
implement said optimization.
PR libstdc++/116440
libstdc++-v3/ChangeLog:
* include/std/tuple (tuple::tuple(const _Elements&...)):
Turn into a template.
* testsuite/20_util/tuple/116440.C: New test.
[1]: See r11-7287-g187d0d5871b1fa and
https://isocpp.org/files/papers/P3606R0.html
---
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..67760471d95 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<class...>
+ 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 00000000000..12259134d25
--- /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);
--
2.49.0.rc1.37.ge969bc8759