diff mbox series

Refactor uses-allocator construction

Message ID 20180815191955.GA19207@redhat.com
State New
Headers show
Series Refactor uses-allocator construction | expand

Commit Message

Jonathan Wakely Aug. 15, 2018, 7:19 p.m. UTC
Remove duplicated logic in experimental::pmr::polymorphic_allocator by
calling the __uses_allocator_construct helper.

Fix bugs in std::pmr::polymorphic_allocator with incorrect SFINAE
constraint and incorrect argument order.

	* include/bits/uses_allocator.h (__uses_allocator_construct): Qualify
	calls to __uses_allocator_construct_impl and __use_alloc.
	* include/experimental/memory_resource
	(polymorphic_allocator::_M_construct): Remove.
	(polymorphic_allocator::construct): Call __uses_allocator_construct.
	Qualify calls to __use_alloc.
	* include/std/memory_resource (polymorphic_allocator::construct): Fix
	type in SFINAE constraint. Use constexpr if instead of tag dispatching
	to _S_construct overloads.
	(polymorphic_allocator::construct(pair<T1, T2>*, ...)): Fix order of
	arguments to _S_construct_p.
	(polymorphic_allocator::_S_construct): Remove.
	(polymorphic_allocator::_S_construct_p): Return allocators by value
	not by reference.
	* include/std/scoped_allocator (scoped_allocator_adaptor::construct):
	Qualify calls to __use_alloc.
	* testsuite/20_util/polymorphic_allocator/construct_pair.cc: New test,
	copied from testsuite/20_util/scoped_allocator/construct_pair.cc.

Tested x86_64-linux, committed to trunk.
diff mbox series

Patch

commit f4e214d819151ce2693dfd992e6e61389923750f
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Wed Aug 15 20:14:05 2018 +0100

    Refactor uses-allocator construction
    
    Remove duplicated logic in experimental::pmr::polymorphic_allocator by
    calling the __uses_allocator_construct helper.
    
    Fix bugs in std::pmr::polymorphic_allocator with incorrect SFINAE
    constraint and incorrect argument order.
    
            * include/bits/uses_allocator.h (__uses_allocator_construct): Qualify
            calls to __uses_allocator_construct_impl and __use_alloc.
            * include/experimental/memory_resource
            (polymorphic_allocator::_M_construct): Remove.
            (polymorphic_allocator::construct): Call __uses_allocator_construct.
            Qualify calls to __use_alloc.
            * include/std/memory_resource (polymorphic_allocator::construct): Fix
            type in SFINAE constraint. Use constexpr if instead of tag dispatching
            to _S_construct overloads.
            (polymorphic_allocator::construct(pair<T1, T2>*, ...)): Fix order of
            arguments to _S_construct_p.
            (polymorphic_allocator::_S_construct): Remove.
            (polymorphic_allocator::_S_construct_p): Return allocators by value
            not by reference.
            * include/std/scoped_allocator (scoped_allocator_adaptor::construct):
            Qualify calls to __use_alloc.
            * testsuite/20_util/polymorphic_allocator/construct_pair.cc: New test,
            copied from testsuite/20_util/scoped_allocator/construct_pair.cc.
            * testsuite/experimental/polymorphic_allocator/1.cc: New test.
            * testsuite/experimental/polymorphic_allocator/construct_pair.cc:
            New test.

diff --git a/libstdc++-v3/include/bits/uses_allocator.h b/libstdc++-v3/include/bits/uses_allocator.h
index 3ef2830bebc..1d313b7e7fd 100644
--- a/libstdc++-v3/include/bits/uses_allocator.h
+++ b/libstdc++-v3/include/bits/uses_allocator.h
@@ -179,8 +179,9 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     void __uses_allocator_construct(const _Alloc& __a, _Tp* __ptr,
 				    _Args&&... __args)
     {
-      __uses_allocator_construct_impl(__use_alloc<_Tp, _Alloc, _Args...>(__a),
-				      __ptr, std::forward<_Args>(__args)...);
+      std::__uses_allocator_construct_impl(
+	  std::__use_alloc<_Tp, _Alloc, _Args...>(__a), __ptr,
+	  std::forward<_Args>(__args)...);
     }
 
 _GLIBCXX_END_NAMESPACE_VERSION
diff --git a/libstdc++-v3/include/experimental/memory_resource b/libstdc++-v3/include/experimental/memory_resource
index 61c9ce0a14a..ccb45bfa335 100644
--- a/libstdc++-v3/include/experimental/memory_resource
+++ b/libstdc++-v3/include/experimental/memory_resource
@@ -123,27 +123,6 @@  namespace pmr {
   template<typename _Tp>
     class polymorphic_allocator
     {
-      using __uses_alloc1_ = __uses_alloc1<memory_resource*>;
-      using __uses_alloc2_ = __uses_alloc2<memory_resource*>;
-
-      template<typename _Tp1, typename... _Args>
-	void
-	_M_construct(__uses_alloc0, _Tp1* __p, _Args&&... __args)
-	{ ::new(__p) _Tp1(std::forward<_Args>(__args)...); }
-
-      template<typename _Tp1, typename... _Args>
-	void
-	_M_construct(__uses_alloc1_, _Tp1* __p, _Args&&...  __args)
-	{
-	  ::new(__p) _Tp1(allocator_arg, this->resource(),
-			  std::forward<_Args>(__args)...);
-	}
-
-      template<typename _Tp1, typename... _Args>
-	void
-	_M_construct(__uses_alloc2_, _Tp1* __p, _Args&&...  __args)
-	{ ::new(__p) _Tp1(std::forward<_Args>(__args)..., this->resource()); }
-
     public:
       using value_type = _Tp;
 
@@ -178,10 +157,8 @@  namespace pmr {
 	void
 	construct(_Tp1* __p, _Args&&... __args)
 	{
-	  memory_resource* const __resource = this->resource();
-	  auto __use_tag
-	    = __use_alloc<_Tp1, memory_resource*, _Args...>(__resource);
-	  _M_construct(__use_tag, __p, std::forward<_Args>(__args)...);
+	  std::__uses_allocator_construct(this->resource(), __p,
+					  std::forward<_Args>(__args)...);
 	}
 
       // Specializations for pair using piecewise construction
@@ -193,9 +170,9 @@  namespace pmr {
 	{
 	  memory_resource* const __resource = this->resource();
 	  auto __x_use_tag =
-	    __use_alloc<_Tp1, memory_resource*, _Args1...>(__resource);
+	    std::__use_alloc<_Tp1, memory_resource*, _Args1...>(__resource);
 	  auto __y_use_tag =
-	    __use_alloc<_Tp2, memory_resource*, _Args2...>(__resource);
+	    std::__use_alloc<_Tp2, memory_resource*, _Args2...>(__resource);
 
 	  ::new(__p) std::pair<_Tp1, _Tp2>(piecewise_construct,
 					   _M_construct_p(__x_use_tag, __x),
@@ -247,6 +224,9 @@  namespace pmr {
       memory_resource* resource() const { return _M_resource; }
 
     private:
+      using __uses_alloc1_ = __uses_alloc1<memory_resource*>;
+      using __uses_alloc2_ = __uses_alloc2<memory_resource*>;
+
       template<typename _Tuple>
 	_Tuple&&
 	_M_construct_p(__uses_alloc0, _Tuple& __t)
diff --git a/libstdc++-v3/include/std/memory_resource b/libstdc++-v3/include/std/memory_resource
index bb4e31551e6..7dc35ae723d 100644
--- a/libstdc++-v3/include/std/memory_resource
+++ b/libstdc++-v3/include/std/memory_resource
@@ -163,14 +163,20 @@  namespace pmr
 
       template<typename _Tp1, typename... _Args>
 	__attribute__((__nonnull__))
-	typename __not_pair<_Tp>::type
+	typename __not_pair<_Tp1>::type
 	construct(_Tp1* __p, _Args&&... __args)
 	{
 	  // _GLIBCXX_RESOLVE_LIB_DEFECTS
 	  // 2969. polymorphic_allocator::construct() shouldn't pass resource()
-	  auto __use_tag
-	    = __use_alloc<_Tp1, polymorphic_allocator, _Args...>(*this);
-	  _S_construct(__use_tag, __p, std::forward<_Args>(__args)...);
+	  using __use_tag
+	    = std::__uses_alloc_t<_Tp1, polymorphic_allocator, _Args...>;
+	  if constexpr (is_base_of_v<__uses_alloc0, __use_tag>)
+	    ::new(__p) _Tp1(std::forward<_Args>(__args)...);
+	  else if constexpr (is_base_of_v<__uses_alloc1_, __use_tag>)
+	    ::new(__p) _Tp1(allocator_arg, *this,
+			    std::forward<_Args>(__args)...);
+	  else
+	    ::new(__p) _Tp1(std::forward<_Args>(__args)..., *this);
 	}
 
       template<typename _Tp1, typename _Tp2,
@@ -188,8 +194,8 @@  namespace pmr
 	  index_sequence_for<_Args2...> __y_i;
 
 	  ::new(__p) pair<_Tp1, _Tp2>(piecewise_construct,
-				      _S_construct_p(__x_tag, __x, __x_i),
-				      _S_construct_p(__y_tag, __y, __y_i));
+				      _S_construct_p(__x_tag, __x_i, __x),
+				      _S_construct_p(__y_tag, __y_i, __y));
 	}
 
       template<typename _Tp1, typename _Tp2>
@@ -247,31 +253,13 @@  namespace pmr
       using __uses_alloc1_ = __uses_alloc1<polymorphic_allocator>;
       using __uses_alloc2_ = __uses_alloc2<polymorphic_allocator>;
 
-      template<typename _Tp1, typename... _Args>
-	static void
-	_S_construct(__uses_alloc0, _Tp1* __p, _Args&&... __args)
-	{ ::new(__p) _Tp1(std::forward<_Args>(__args)...); }
-
-      template<typename _Tp1, typename... _Args>
-	static void
-	_S_construct(__uses_alloc1_ __ua, _Tp1* __p, _Args&&... __args)
-	{
-	  ::new(__p) _Tp1(allocator_arg, *__ua._M_a,
-			  std::forward<_Args>(__args)...);
-	}
-
-      template<typename _Tp1, typename... _Args>
-	static void
-	_S_construct(__uses_alloc2_ __ua, _Tp1* __p, _Args&&... __args)
-	{ ::new(__p) _Tp1(std::forward<_Args>(__args)..., *__ua._M_a); }
-
       template<typename _Ind, typename... _Args>
 	static tuple<_Args&&...>
 	_S_construct_p(__uses_alloc0, _Ind, tuple<_Args...>& __t)
 	{ return std::move(__t); }
 
       template<size_t... _Ind, typename... _Args>
-	static tuple<allocator_arg_t, polymorphic_allocator&, _Args&&...>
+	static tuple<allocator_arg_t, polymorphic_allocator, _Args&&...>
 	_S_construct_p(__uses_alloc1_ __ua, index_sequence<_Ind...>,
 		       tuple<_Args...>& __t)
 	{
@@ -281,7 +269,7 @@  namespace pmr
 	}
 
       template<size_t... _Ind, typename... _Args>
-	static tuple<_Args&&..., polymorphic_allocator&>
+	static tuple<_Args&&..., polymorphic_allocator>
 	_S_construct_p(__uses_alloc2_ __ua, index_sequence<_Ind...>,
 		       tuple<_Args...>& __t)
 	{ return { std::get<_Ind>(std::move(__t))..., *__ua._M_a }; }
diff --git a/libstdc++-v3/include/std/scoped_allocator b/libstdc++-v3/include/std/scoped_allocator
index ea62f113517..f1f5dd93252 100644
--- a/libstdc++-v3/include/std/scoped_allocator
+++ b/libstdc++-v3/include/std/scoped_allocator
@@ -361,7 +361,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	{
 	  auto& __inner = inner_allocator();
 	  auto __use_tag
-	    = __use_alloc<_Tp, inner_allocator_type, _Args...>(__inner);
+	    = std::__use_alloc<_Tp, inner_allocator_type, _Args...>(__inner);
 	  _M_construct(__use_tag, __p, std::forward<_Args>(__args)...);
 	}
 
@@ -375,9 +375,9 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  // 2203.  wrong argument types for piecewise construction
 	  auto& __inner = inner_allocator();
 	  auto __x_use_tag
-	    = __use_alloc<_T1, inner_allocator_type, _Args1...>(__inner);
+	    = std::__use_alloc<_T1, inner_allocator_type, _Args1...>(__inner);
 	  auto __y_use_tag
-	    = __use_alloc<_T2, inner_allocator_type, _Args2...>(__inner);
+	    = std::__use_alloc<_T2, inner_allocator_type, _Args2...>(__inner);
 	  typename _Build_index_tuple<sizeof...(_Args1)>::__type __x_indices;
 	  typename _Build_index_tuple<sizeof...(_Args2)>::__type __y_indices;
 	  typedef __outermost_alloc_traits<scoped_allocator_adaptor> _O_traits;
diff --git a/libstdc++-v3/testsuite/20_util/polymorphic_allocator/construct_pair.cc b/libstdc++-v3/testsuite/20_util/polymorphic_allocator/construct_pair.cc
new file mode 100644
index 00000000000..d9cab043c0b
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/polymorphic_allocator/construct_pair.cc
@@ -0,0 +1,110 @@ 
+// Copyright (C) 2016-2018 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-options "-std=gnu++17" }
+// { dg-do run { target c++17 } }
+
+#include <memory_resource>
+#include <utility>
+#include <tuple>
+
+struct do_not_copy {
+  do_not_copy() = default;
+  do_not_copy(const do_not_copy&) { throw 1; }
+};
+
+void
+test01()
+{
+  struct X {
+    X(do_not_copy&&) { }
+  };
+
+  using pair = std::pair<X, int>;
+  std::pmr::polymorphic_allocator<pair> a;
+  auto ptr = a.allocate(1);
+  a.construct(ptr, std::piecewise_construct,
+      std::tuple<do_not_copy>{}, std::make_tuple(1));
+  a.deallocate(ptr, 1);
+}
+
+void
+test02()
+{
+  struct X {
+    using allocator_type = std::pmr::polymorphic_allocator<int>;
+    X(do_not_copy&&, const allocator_type&) { }
+  };
+
+  using pair = std::pair<X, int>;
+  std::pmr::polymorphic_allocator<pair> a;
+  auto ptr = a.allocate(1);
+  a.construct(ptr, std::piecewise_construct,
+      std::tuple<do_not_copy>{}, std::make_tuple(1));
+  a.deallocate(ptr, 1);
+}
+
+void
+test03()
+{
+  struct X {
+    using allocator_type = std::pmr::polymorphic_allocator<int>;
+    X(std::allocator_arg_t, const allocator_type&, do_not_copy&&) { }
+  };
+
+  using pair = std::pair<X, int>;
+  std::pmr::polymorphic_allocator<pair> a;
+  auto ptr = a.allocate(1);
+  a.construct(ptr, std::piecewise_construct,
+      std::tuple<do_not_copy>{}, std::make_tuple(1));
+  a.deallocate(ptr, 1);
+}
+
+void
+test04()
+{
+  struct X
+  {
+    using allocator_type = std::pmr::polymorphic_allocator<int>;
+    X() = default;
+    X(const X&) { throw 1; }
+    X(const X&, const allocator_type&) { }
+  };
+
+  struct Y
+  {
+    using allocator_type = std::pmr::polymorphic_allocator<int>;
+    Y() = default;
+    Y(const Y&) = delete;
+    Y(std::allocator_arg_t, const allocator_type&, const Y&) { }
+  };
+
+  using pair_type = std::pair<X, Y>;
+  std::pmr::polymorphic_allocator<pair_type> a;
+  auto ptr = a.allocate(1);
+  /* not const */ pair_type p;
+  a.construct(ptr, p); // LWG 2975
+  a.deallocate(ptr, 1);
+}
+
+int main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+}
diff --git a/libstdc++-v3/testsuite/experimental/polymorphic_allocator/1.cc b/libstdc++-v3/testsuite/experimental/polymorphic_allocator/1.cc
new file mode 100644
index 00000000000..90463471719
--- /dev/null
+++ b/libstdc++-v3/testsuite/experimental/polymorphic_allocator/1.cc
@@ -0,0 +1,37 @@ 
+// Copyright (C) 2018 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++14 } }
+
+#include <experimental/memory_resource>
+
+struct X { int i = 0; };
+
+using test_type = std::experimental::pmr::polymorphic_allocator<X>;
+
+static_assert(std::is_default_constructible<test_type>{}, "");
+static_assert(std::is_destructible<test_type>{}, "");
+static_assert(std::is_copy_constructible<test_type>{}, "");
+// N.B. std::pmr::polymorphic_allocator is not assignable, see p0337r0
+static_assert(std::is_copy_assignable<test_type>{}, "");
+static_assert(std::is_constructible<test_type,
+    std::experimental::pmr::memory_resource*>{}, "");
+
+static_assert(std::is_same<test_type::value_type, X>{}, "");
+
+static_assert(!std::is_polymorphic<test_type>{}, "");
+static_assert(!std::is_final<test_type>{}, "");
diff --git a/libstdc++-v3/testsuite/experimental/polymorphic_allocator/construct_pair.cc b/libstdc++-v3/testsuite/experimental/polymorphic_allocator/construct_pair.cc
new file mode 100644
index 00000000000..1708f5e329b
--- /dev/null
+++ b/libstdc++-v3/testsuite/experimental/polymorphic_allocator/construct_pair.cc
@@ -0,0 +1,78 @@ 
+// Copyright (C) 2018 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 run { target c++14 } }
+
+#include <experimental/memory_resource>
+#include <utility>
+#include <tuple>
+
+struct A { };
+
+void
+test01()
+{
+  struct X {
+    X(A&&) { }
+  };
+
+  using pair = std::pair<X, int>;
+  std::experimental::pmr::polymorphic_allocator<pair> a;
+  auto ptr = a.allocate(1);
+  a.construct(ptr, std::piecewise_construct,
+      std::tuple<A>{}, std::make_tuple(1));
+  a.deallocate(ptr, 1);
+}
+
+void
+test02()
+{
+  struct X {
+    using allocator_type = std::experimental::pmr::polymorphic_allocator<int>;
+    X(A&&, const allocator_type&) { }
+  };
+
+  using pair = std::pair<X, int>;
+  std::experimental::pmr::polymorphic_allocator<pair> a;
+  auto ptr = a.allocate(1);
+  a.construct(ptr, std::piecewise_construct,
+      std::tuple<A>{}, std::make_tuple(1));
+  a.deallocate(ptr, 1);
+}
+
+void
+test03()
+{
+  struct X {
+    using allocator_type = std::experimental::pmr::polymorphic_allocator<int>;
+    X(std::allocator_arg_t, const allocator_type&, A&&) { }
+  };
+
+  using pair = std::pair<X, int>;
+  std::experimental::pmr::polymorphic_allocator<pair> a;
+  auto ptr = a.allocate(1);
+  a.construct(ptr, std::piecewise_construct,
+      std::tuple<A>{}, std::make_tuple(1));
+  a.deallocate(ptr, 1);
+}
+
+int main()
+{
+  test01();
+  test02();
+  test03();
+}