Author: ericwf Date: Wed Jan 24 16:02:48 2018 New Revision: 323390 URL: http://llvm.org/viewvc/llvm-project?rev=323390&view=rev Log: 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. Modified: libcxx/trunk/include/list libcxx/trunk/test/libcxx/debug/containers/db_sequence_container_iterators.pass.cpp Modified: libcxx/trunk/include/list URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/list?rev=323390&r1=323389&r2=323390&view=diff ============================================================================== --- libcxx/trunk/include/list (original) +++ libcxx/trunk/include/list Wed Jan 24 16:02:48 2018 @@ -2058,15 +2058,15 @@ list<_Tp, _Alloc>::splice(const_iterator #endif if (__f != __l) { + __link_pointer __first = __f.__ptr_; + --__l; + __link_pointer __last = __l.__ptr_; if (this != &__c) { - size_type __s = _VSTD::distance(__f, __l); + size_type __s = _VSTD::distance(__f, __l) + 1; __c.__sz() -= __s; base::__sz() += __s; } - __link_pointer __first = __f.__ptr_; - --__l; - __link_pointer __last = __l.__ptr_; base::__unlink_nodes(__first, __last); __link_nodes(__p.__ptr_, __first, __last); #if _LIBCPP_DEBUG_LEVEL >= 2 Modified: libcxx/trunk/test/libcxx/debug/containers/db_sequence_container_iterators.pass.cpp URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/libcxx/debug/containers/db_sequence_container_iterators.pass.cpp?rev=323390&r1=323389&r2=323390&view=diff ============================================================================== --- libcxx/trunk/test/libcxx/debug/containers/db_sequence_container_iterators.pass.cpp (original) +++ libcxx/trunk/test/libcxx/debug/containers/db_sequence_container_iterators.pass.cpp Wed Jan 24 16:02:48 2018 @@ -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(<first-elem>)"); + { + 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(<first-elem>)"); + { + 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(<first-elem>)"); + { + 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{}); _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits