diff mbox series

[committed] libstdc++: Remove inheritance from elements in std::tuple

Message ID 20200817142910.GA3885220@redhat.com
State New
Headers show
Series [committed] libstdc++: Remove inheritance from elements in std::tuple | expand

Commit Message

Jonathan Wakely Aug. 17, 2020, 2:29 p.m. UTC
This fixes a number of std::tuple bugs by no longer making use of the
empty base-class optimization. By using the C++20 [[no_unique_address]]
attribute we can always store the element as a data member, while still
compressing the layout of tuples containing empty types.

Since we no longer use inheritance we could also apply the compression
optimization for final types and for tuples of tuples, but doing so
would be an ABI break.

Using [[no_unique_address]] more liberally for the unstable std::__8
configuration is left for a later date. There may be reasons not to
apply the attribute unconditionally, e.g. see the discussion about
guaranteed elision in PR 94062.

libstdc++-v3/ChangeLog:

	PR libstdc++/55713
	PR libstdc++/71096
	PR libstdc++/93147
	* include/std/tuple [__has_cpp_attribute(no_unique_address)]
	(_Head_base<Idx, Head, true>): New definition of the partial
	specialization, using [[no_unique_address]] instead of
	inheritance.
	* testsuite/libstdc++-prettyprinters/48362.cc: Adjust expected
	output.
	* testsuite/20_util/tuple/comparison_operators/93147.cc: New test.
	* testsuite/20_util/tuple/creation_functions/55713.cc: New test.
	* testsuite/20_util/tuple/element_access/71096.cc: New test.

Tested powerpc64le-linux. Committed to trunk.
commit 91e6226f880b048275a7ceedef716e159c7cefd9
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Fri Aug 7 17:13:56 2020

    libstdc++: Remove inheritance from elements in std::tuple
    
    This fixes a number of std::tuple bugs by no longer making use of the
    empty base-class optimization. By using the C++20 [[no_unique_address]]
    attribute we can always store the element as a data member, while still
    compressing the layout of tuples containing empty types.
    
    Since we no longer use inheritance we could also apply the compression
    optimization for final types and for tuples of tuples, but doing so
    would be an ABI break.
    
    Using [[no_unique_address]] more liberally for the unstable std::__8
    configuration is left for a later date. There may be reasons not to
    apply the attribute unconditionally, e.g. see the discussion about
    guaranteed elision in PR 94062.
    
    libstdc++-v3/ChangeLog:
    
            PR libstdc++/55713
            PR libstdc++/71096
            PR libstdc++/93147
            * include/std/tuple [__has_cpp_attribute(no_unique_address)]
            (_Head_base<Idx, Head, true>): New definition of the partial
            specialization, using [[no_unique_address]] instead of
            inheritance.
            * testsuite/libstdc++-prettyprinters/48362.cc: Adjust expected
            output.
            * testsuite/20_util/tuple/comparison_operators/93147.cc: New test.
            * testsuite/20_util/tuple/creation_functions/55713.cc: New test.
            * testsuite/20_util/tuple/element_access/71096.cc: New test.
diff mbox series

Patch

diff --git a/libstdc++-v3/include/std/tuple b/libstdc++-v3/include/std/tuple
index 0dc11768a90..d4a35f0fe7f 100644
--- a/libstdc++-v3/include/std/tuple
+++ b/libstdc++-v3/include/std/tuple
@@ -73,6 +73,58 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	   bool = __empty_not_final<_Head>::value>
     struct _Head_base;
 
+#if __has_cpp_attribute(no_unique_address)
+  template<size_t _Idx, typename _Head>
+    struct _Head_base<_Idx, _Head, true>
+    {
+      constexpr _Head_base()
+      : _M_head_impl() { }
+
+      constexpr _Head_base(const _Head& __h)
+      : _M_head_impl(__h) { }
+
+      constexpr _Head_base(const _Head_base&) = default;
+      constexpr _Head_base(_Head_base&&) = default;
+
+      template<typename _UHead>
+	constexpr _Head_base(_UHead&& __h)
+	: _M_head_impl(std::forward<_UHead>(__h)) { }
+
+      _GLIBCXX20_CONSTEXPR
+      _Head_base(allocator_arg_t, __uses_alloc0)
+      : _M_head_impl() { }
+
+      template<typename _Alloc>
+	_Head_base(allocator_arg_t, __uses_alloc1<_Alloc> __a)
+	: _M_head_impl(allocator_arg, *__a._M_a) { }
+
+      template<typename _Alloc>
+	_Head_base(allocator_arg_t, __uses_alloc2<_Alloc> __a)
+	: _M_head_impl(*__a._M_a) { }
+
+      template<typename _UHead>
+	_GLIBCXX20_CONSTEXPR
+	_Head_base(__uses_alloc0, _UHead&& __uhead)
+	: _M_head_impl(std::forward<_UHead>(__uhead)) { }
+
+      template<typename _Alloc, typename _UHead>
+	_Head_base(__uses_alloc1<_Alloc> __a, _UHead&& __uhead)
+	: _M_head_impl(allocator_arg, *__a._M_a, std::forward<_UHead>(__uhead))
+	{ }
+
+      template<typename _Alloc, typename _UHead>
+	_Head_base(__uses_alloc2<_Alloc> __a, _UHead&& __uhead)
+	: _M_head_impl(std::forward<_UHead>(__uhead), *__a._M_a) { }
+
+      static constexpr _Head&
+      _M_head(_Head_base& __b) noexcept { return __b._M_head_impl; }
+
+      static constexpr const _Head&
+      _M_head(const _Head_base& __b) noexcept { return __b._M_head_impl; }
+
+      [[no_unique_address]] _Head _M_head_impl;
+    };
+#else
   template<size_t _Idx, typename _Head>
     struct _Head_base<_Idx, _Head, true>
     : public _Head
@@ -119,6 +171,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       static constexpr const _Head&
       _M_head(const _Head_base& __b) noexcept { return __b; }
     };
+#endif
 
   template<size_t _Idx, typename _Head>
     struct _Head_base<_Idx, _Head, false>
diff --git a/libstdc++-v3/testsuite/20_util/tuple/comparison_operators/93147.cc b/libstdc++-v3/testsuite/20_util/tuple/comparison_operators/93147.cc
new file mode 100644
index 00000000000..b6c61d5717b
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/tuple/comparison_operators/93147.cc
@@ -0,0 +1,36 @@ 
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library 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, or (at your option)
+// any later version.
+
+// This library 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 library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-do compile { target c++11 } }
+
+
+#include <tuple>
+
+struct A {
+    bool operator == (A) const { return true; }
+};
+
+struct B {
+    bool operator == (B) const { return true; }
+};
+
+using Tuple = std::tuple<A, B>;
+
+bool example(Tuple a, Tuple b)
+{
+    return a == b; // PR libstdc++/93147
+}
diff --git a/libstdc++-v3/testsuite/20_util/tuple/creation_functions/55713.cc b/libstdc++-v3/testsuite/20_util/tuple/creation_functions/55713.cc
new file mode 100644
index 00000000000..05a90198fde
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/tuple/creation_functions/55713.cc
@@ -0,0 +1,33 @@ 
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library 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, or (at your option)
+// any later version.
+
+// This library 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 library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-do compile { target c++11 } }
+
+#include <tuple>
+
+struct A { };
+
+void f(A);
+
+struct B { B(std::tuple<A>); };
+
+void f(B);
+
+void test01()
+{
+  f(std::make_tuple(A())); // PR libstdc++/55713
+}
diff --git a/libstdc++-v3/testsuite/20_util/tuple/element_access/71096.cc b/libstdc++-v3/testsuite/20_util/tuple/element_access/71096.cc
new file mode 100644
index 00000000000..6fa882f25d5
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/tuple/element_access/71096.cc
@@ -0,0 +1,30 @@ 
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library 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, or (at your option)
+// any later version.
+
+// This library 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 library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-do compile { target c++11 } }
+
+#include <tuple>
+
+struct A {};
+struct B : std::tuple<A> {};
+
+void
+test01()
+{
+  std::tuple<B> t;
+  std::get<0>(t); // PR libstdc++/71096
+}
diff --git a/libstdc++-v3/testsuite/libstdc++-prettyprinters/48362.cc b/libstdc++-v3/testsuite/libstdc++-prettyprinters/48362.cc
index 85add53fe5a..f8ff6fba5a7 100644
--- a/libstdc++-v3/testsuite/libstdc++-prettyprinters/48362.cc
+++ b/libstdc++-v3/testsuite/libstdc++-prettyprinters/48362.cc
@@ -29,7 +29,7 @@  main()
 // { dg-final { note-test t1 {empty std::tuple} } }
 
   std::tuple<std::string, int, std::tuple<>> t2{ "Johnny", 5, {} };
-// { dg-final { regexp-test t2 {std::tuple containing = {\[1\] = "Johnny", \[2\] = 5, \[3\] = {<std::(__8::)?tuple<>> = empty std::tuple, <No data fields>}}} } }
+// { dg-final { regexp-test t2 {std::tuple containing = {\[1\] = "Johnny", \[2\] = 5, \[3\] = empty std::tuple}} } }
 
   std::cout << "\n";
   return 0; // Mark SPOT