From 8864da5e7823f79f801512ec4dbcca89feff7a23 Mon Sep 17 00:00:00 2001 From: Eric Fiselier Date: Thu, 25 Jan 2018 00:02:48 +0000 Subject: [PATCH] Fix PR35564 - std::list splice/erase incorrectly throw in debug mode. There was a bug in the implementation of splice where the container sizes were updated before decrementing one of the iterators. Afterwards, the result of decrementing the iterator was flagged as UB by the debug implementation because the container was reported to be empty. This patch fixes that bug by delaying the updating of the container sizes until after the iterators have been correctly constructed. git-svn-id: https://llvm.org/svn/llvm-project/libcxx/trunk@323390 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/list | 12 ++-- .../db_sequence_container_iterators.pass.cpp | 57 +++++++++++++++++++ 2 files changed, 63 insertions(+), 6 deletions(-) diff --git a/include/list b/include/list index 32e9a27bd..f884b1681 100644 --- a/include/list +++ b/include/list @@ -2058,15 +2058,15 @@ list<_Tp, _Alloc>::splice(const_iterator __p, list& __c, const_iterator __f, con #endif if (__f != __l) { - if (this != &__c) - { - size_type __s = _VSTD::distance(__f, __l); - __c.__sz() -= __s; - base::__sz() += __s; - } __link_pointer __first = __f.__ptr_; --__l; __link_pointer __last = __l.__ptr_; + if (this != &__c) + { + size_type __s = _VSTD::distance(__f, __l) + 1; + __c.__sz() -= __s; + base::__sz() += __s; + } base::__unlink_nodes(__first, __last); __link_nodes(__p.__ptr_, __first, __last); #if _LIBCPP_DEBUG_LEVEL >= 2 diff --git a/test/libcxx/debug/containers/db_sequence_container_iterators.pass.cpp b/test/libcxx/debug/containers/db_sequence_container_iterators.pass.cpp index 3ae009a8e..57d16c272 100644 --- a/test/libcxx/debug/containers/db_sequence_container_iterators.pass.cpp +++ b/test/libcxx/debug/containers/db_sequence_container_iterators.pass.cpp @@ -42,6 +42,7 @@ public: Base::run(); try { FrontOnEmptyContainer(); + if constexpr (CT != CT_ForwardList) { AssignInvalidates(); BackOnEmptyContainer(); @@ -50,6 +51,8 @@ public: InsertIterIterIter(); EmplaceIterValue(); EraseIterIter(); + } else { + SpliceFirstElemAfter(); } if constexpr (CT == CT_Vector || CT == CT_Deque || CT == CT_List) { PopBack(); @@ -57,12 +60,66 @@ public: if constexpr (CT == CT_List || CT == CT_Deque) { PopFront(); // FIXME: Run with forward list as well } + if constexpr (CT == CT_List || CT == CT_ForwardList) { + RemoveFirstElem(); + } + if constexpr (CT == CT_List) { + SpliceFirstElem(); + } } catch (...) { assert(false && "uncaught debug exception"); } } private: + static void RemoveFirstElem() { + // See llvm.org/PR35564 + CHECKPOINT("remove()"); + { + Container C = makeContainer(1); + auto FirstVal = *(C.begin()); + C.remove(FirstVal); + assert(C.empty()); + } + { + Container C = {1, 1, 1, 1}; + auto FirstVal = *(C.begin()); + C.remove(FirstVal); + assert(C.empty()); + } + } + + static void SpliceFirstElem() { + // See llvm.org/PR35564 + CHECKPOINT("splice()"); + { + Container C = makeContainer(1); + Container C2; + C2.splice(C2.end(), C, C.begin(), ++C.begin()); + } + { + Container C = makeContainer(1); + Container C2; + C2.splice(C2.end(), C, C.begin()); + } + } + + + static void SpliceFirstElemAfter() { + // See llvm.org/PR35564 + CHECKPOINT("splice()"); + { + Container C = makeContainer(1); + Container C2; + C2.splice_after(C2.begin(), C, C.begin(), ++C.begin()); + } + { + Container C = makeContainer(1); + Container C2; + C2.splice_after(C2.begin(), C, C.begin()); + } + } + static void AssignInvalidates() { CHECKPOINT("assign(Size, Value)"); Container C(allocator_type{});