This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [patch libstdc++] Fix assignability check in uninitialized_copy


On 28/12/14 13:47 +0100, Eelis wrote:
On 2014-12-28 00:18, Eelis wrote:
Trivial fix attached.

Please don't commit this patch.

I just noticed that the assignability test is wrong in an additional way: it should look at assignability of /output/ elements, not /input/ elements.

As a result, this code is currently rejected (because uninitialized_copy doesn't notice in time that chars can't be assigned to Xs):

	struct X
	{
		X() = default;
		X(char) {}
		X(X const &) = default;
		X & operator=(X const&) = default;

		X & operator=(char) = delete;
	};

	#include <memory>

	int main()
	{
		char a[100];
		X b[100];

		std::uninitialized_copy(a, a+10, b);
	}

Updated fix attached. With it, the code is accepted again. Testing as we speak.

Index: stl_uninitialized.h
===================================================================
--- stl_uninitialized.h	(revision 219070)
+++ stl_uninitialized.h	(working copy)
@@ -104,30 +104,30 @@
  */
  template<typename _InputIterator, typename _ForwardIterator>
    inline _ForwardIterator
    uninitialized_copy(_InputIterator __first, _InputIterator __last,
		       _ForwardIterator __result)
    {
      typedef typename iterator_traits<_InputIterator>::value_type
	_ValueType1;
      typedef typename iterator_traits<_ForwardIterator>::value_type
	_ValueType2;
#if __cplusplus < 201103L
      const bool __assignable = true;
#else
      // trivial types can have deleted assignment
-      typedef typename iterator_traits<_InputIterator>::reference _RefType;
-      const bool __assignable = is_assignable<_ValueType1, _RefType>::value;
+      typedef typename iterator_traits<_ForwardIterator>::reference _RefType;
+      const bool __assignable = is_assignable<_RefType, _ValueType1>::value;
#endif

This is still wrong, because it tests for assigning an rvalue to the
result sequence, but dereferencing the _InputIterator doesn't
necessarily produce an rvalue, and the output type might behave
differently for assignment from lvalues and rvalues.

My first attempt to fix it was simply:

#else
      // trivial types can have deleted assignment
      typedef typename iterator_traits<_InputIterator>::reference _RefType;
-      const bool __assignable = is_assignable<_ValueType1, _RefType>::value;
+      const bool __assignable = is_assignable<_ValueType2, _RefType>::value;
#endif


But even that is wrong if the target type has a ref-qualified
assignment from the source type. The attached patch should be correct.

Tested x86_64-linux, committed to trunk and 4.9.

commit 8ba322dd71cca830b3641002b4c1bf132f195741
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Fri Jan 9 15:51:20 2015 +0000

    	PR libstdc++/64476
    	* include/bits/stl_uninitialized.h (uninitialized_copy): Fix
    	is_assignable arguments.
    	* testsuite/20_util/specialized_algorithms/uninitialized_copy/64476.cc:
    	New.

diff --git a/libstdc++-v3/include/bits/stl_uninitialized.h b/libstdc++-v3/include/bits/stl_uninitialized.h
index 00659e9..61a1561 100644
--- a/libstdc++-v3/include/bits/stl_uninitialized.h
+++ b/libstdc++-v3/include/bits/stl_uninitialized.h
@@ -115,8 +115,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       const bool __assignable = true;
 #else
       // trivial types can have deleted assignment
-      typedef typename iterator_traits<_InputIterator>::reference _RefType;
-      const bool __assignable = is_assignable<_ValueType1, _RefType>::value;
+      typedef typename iterator_traits<_InputIterator>::reference _RefType1;
+      typedef typename iterator_traits<_ForwardIterator>::reference _RefType2;
+      const bool __assignable = is_assignable<_RefType2, _RefType1>::value;
 #endif
 
       return std::__uninitialized_copy<__is_trivial(_ValueType1)
diff --git a/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_copy/64476.cc b/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_copy/64476.cc
new file mode 100644
index 0000000..6369b17
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_copy/64476.cc
@@ -0,0 +1,65 @@
+// Copyright (C) 2015 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++11" }
+
+#include <memory>
+#include <testsuite_hooks.h>
+
+struct X
+{
+  X() = default;
+  X(X const &) = default;
+  X& operator=(X const&) = delete;
+};
+
+static_assert(__is_trivial(X), "X is trivial");
+
+int constructed = 0;
+int assigned = 0;
+
+struct Y
+{
+  Y() = default;
+  Y(Y const &) = default;
+  Y& operator=(Y const&) = default;
+
+  Y(const X&) { ++constructed; }
+  Y& operator=(const X&)& { ++assigned; return *this; }
+  Y& operator=(const X&)&& = delete;
+  Y& operator=(X&&) = delete;
+};
+
+static_assert(__is_trivial(Y), "Y is trivial");
+
+void
+test01()
+{
+  X a[100];
+  Y b[100];
+
+  std::uninitialized_copy(a, a+10, b);
+
+  VERIFY(constructed == 0);
+  VERIFY(assigned == 10);
+}
+
+int
+main()
+{
+  test01();
+}

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]