[libcxx-commits] [libcxx] 2c98c6e - [libcxx] LWG4172 fix self-move-assignment in {unique|shared}_lock (#129542)

via libcxx-commits libcxx-commits at lists.llvm.org
Thu Dec 18 23:06:43 PST 2025


Author: Mohamed Atef
Date: 2025-12-19T15:06:39+08:00
New Revision: 2c98c6ee0ec332060993ef1b529c3d6aa605d1d9

URL: https://github.com/llvm/llvm-project/commit/2c98c6ee0ec332060993ef1b529c3d6aa605d1d9
DIFF: https://github.com/llvm/llvm-project/commit/2c98c6ee0ec332060993ef1b529c3d6aa605d1d9.diff

LOG: [libcxx] LWG4172 fix self-move-assignment in {unique|shared}_lock (#129542)

Fixes: https://github.com/llvm/llvm-project/issues/127861

---------

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>

Added: 
    

Modified: 
    libcxx/docs/Status/Cxx2cIssues.csv
    libcxx/include/__mutex/unique_lock.h
    libcxx/include/shared_mutex
    libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.shared/thread.lock.shared.cons/move_assign.pass.cpp
    libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.cons/move_assign.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/Status/Cxx2cIssues.csv b/libcxx/docs/Status/Cxx2cIssues.csv
index 482a9139f15cf..862a485f0a273 100644
--- a/libcxx/docs/Status/Cxx2cIssues.csv
+++ b/libcxx/docs/Status/Cxx2cIssues.csv
@@ -113,7 +113,7 @@
 "","","","","","",""
 "`LWG3578 <https://wg21.link/LWG3578>`__","Iterator SCARYness in the context of associative container merging","2025-02 (Hagenberg)","","","`#127859 <https://github.com/llvm/llvm-project/issues/127859>`__",""
 "`LWG3956 <https://wg21.link/LWG3956>`__","``chrono::parse`` uses ``from_stream`` as a customization point","2025-02 (Hagenberg)","","","`#127860 <https://github.com/llvm/llvm-project/issues/127860>`__",""
-"`LWG4172 <https://wg21.link/LWG4172>`__","``unique_lock`` self-move-assignment is broken","2025-02 (Hagenberg)","","","`#127861 <https://github.com/llvm/llvm-project/issues/127861>`__",""
+"`LWG4172 <https://wg21.link/LWG4172>`__","``unique_lock`` self-move-assignment is broken","2025-02 (Hagenberg)","|Complete|","22","`#127861 <https://github.com/llvm/llvm-project/issues/127861>`__",""
 "`LWG4175 <https://wg21.link/LWG4175>`__","``get_env()`` specified in terms of ``as_const()`` but this doesn't work with rvalue senders","2025-02 (Hagenberg)","","","`#127862 <https://github.com/llvm/llvm-project/issues/127862>`__",""
 "`LWG4179 <https://wg21.link/LWG4179>`__","Wrong range in ``[alg.search]``","2025-02 (Hagenberg)","","","`#127863 <https://github.com/llvm/llvm-project/issues/127863>`__",""
 "`LWG4186 <https://wg21.link/LWG4186>`__","``regex_traits::transform_primary`` mistakenly detects ``typeid`` of a function","2025-02 (Hagenberg)","","","`#127864 <https://github.com/llvm/llvm-project/issues/127864>`__",""

diff  --git a/libcxx/include/__mutex/unique_lock.h b/libcxx/include/__mutex/unique_lock.h
index aea93eb9b8c9b..6968922639673 100644
--- a/libcxx/include/__mutex/unique_lock.h
+++ b/libcxx/include/__mutex/unique_lock.h
@@ -15,6 +15,7 @@
 #include <__memory/addressof.h>
 #include <__mutex/tag_types.h>
 #include <__system_error/throw_system_error.h>
+#include <__utility/move.h>
 #include <__utility/swap.h>
 #include <cerrno>
 
@@ -22,6 +23,9 @@
 #  pragma GCC system_header
 #endif
 
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _Mutex>
@@ -74,13 +78,8 @@ class unique_lock {
   }
 
   _LIBCPP_HIDE_FROM_ABI unique_lock& operator=(unique_lock&& __u) _NOEXCEPT {
-    if (__owns_)
-      __m_->unlock();
-
-    __m_        = __u.__m_;
-    __owns_     = __u.__owns_;
-    __u.__m_    = nullptr;
-    __u.__owns_ = false;
+    if (this != std::addressof(__u))
+      unique_lock(std::move(__u)).swap(*this);
     return *this;
   }
 
@@ -170,4 +169,6 @@ inline _LIBCPP_HIDE_FROM_ABI void swap(unique_lock<_Mutex>& __x, unique_lock<_Mu
 
 _LIBCPP_END_NAMESPACE_STD
 
+_LIBCPP_POP_MACROS
+
 #endif // _LIBCPP___MUTEX_UNIQUE_LOCK_H

diff  --git a/libcxx/include/shared_mutex b/libcxx/include/shared_mutex
index 8c02e348e4de7..028bbf5650254 100644
--- a/libcxx/include/shared_mutex
+++ b/libcxx/include/shared_mutex
@@ -138,6 +138,7 @@ template <class Mutex>
 #    include <__mutex/tag_types.h>
 #    include <__mutex/unique_lock.h>
 #    include <__system_error/throw_system_error.h>
+#    include <__utility/move.h>
 #    include <__utility/swap.h>
 #    include <cerrno>
 #    include <version>
@@ -340,14 +341,8 @@ public:
   }
 
   _LIBCPP_HIDE_FROM_ABI shared_lock& operator=(shared_lock&& __u) _NOEXCEPT {
-    if (__owns_)
-      __m_->unlock_shared();
-    __m_        = nullptr;
-    __owns_     = false;
-    __m_        = __u.__m_;
-    __owns_     = __u.__owns_;
-    __u.__m_    = nullptr;
-    __u.__owns_ = false;
+    if (this != std::addressof(__u))
+      shared_lock(std::move(__u)).swap(*this);
     return *this;
   }
 

diff  --git a/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.shared/thread.lock.shared.cons/move_assign.pass.cpp b/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.shared/thread.lock.shared.cons/move_assign.pass.cpp
index 6d7838e8c6c95..2e1b46ae426e3 100644
--- a/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.shared/thread.lock.shared.cons/move_assign.pass.cpp
+++ b/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.shared/thread.lock.shared.cons/move_assign.pass.cpp
@@ -5,7 +5,9 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-//
+
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
 // UNSUPPORTED: no-threads
 // UNSUPPORTED: c++03, c++11
 
@@ -13,18 +15,17 @@
 
 // template <class Mutex> class shared_lock;
 
-// shared_lock& operator=(shared_lock&& u);
+// shared_lock& operator=(shared_lock&& u) noexcept;
 
-#include <shared_mutex>
 #include <cassert>
-#include "nasty_containers.h"
+#include <shared_mutex>
+#include <type_traits>
 
+#include "nasty_containers.h"
 #include "test_macros.h"
 
-
-int main(int, char**)
-{
-    {
+int main(int, char**) {
+  {
     typedef std::shared_timed_mutex M;
     M m0;
     M m1;
@@ -35,8 +36,10 @@ int main(int, char**)
     assert(lk1.owns_lock() == true);
     assert(lk0.mutex() == nullptr);
     assert(lk0.owns_lock() == false);
-    }
-    {
+
+    static_assert(std::is_nothrow_move_assignable<std::shared_lock<M> >::value, "");
+  }
+  {
     typedef nasty_mutex M;
     M m0;
     M m1;
@@ -47,7 +50,18 @@ int main(int, char**)
     assert(lk1.owns_lock() == true);
     assert(lk0.mutex() == nullptr);
     assert(lk0.owns_lock() == false);
-    }
+
+    static_assert(std::is_nothrow_move_assignable<std::shared_lock<M> >::value, "");
+  }
+  {
+    // Test self move-assignment (LWG4172)
+    typedef std::shared_timed_mutex M;
+    M m0;
+    std::shared_lock<M> lk0(m0);
+    lk0 = std::move(lk0);
+    assert(lk0.mutex() == std::addressof(m0));
+    assert(lk0.owns_lock() == true);
+  }
 
   return 0;
 }

diff  --git a/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.cons/move_assign.pass.cpp b/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.cons/move_assign.pass.cpp
index 588d8332c4164..eaa95d3c74c53 100644
--- a/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.cons/move_assign.pass.cpp
+++ b/libcxx/test/std/thread/thread.mutex/thread.lock/thread.lock.unique/thread.lock.unique.cons/move_assign.pass.cpp
@@ -6,31 +6,52 @@
 //
 //===----------------------------------------------------------------------===//
 
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
 // <mutex>
 
 // template <class Mutex> class unique_lock;
 
-// unique_lock& operator=(unique_lock&& u);
+// unique_lock& operator=(unique_lock&& u) noexcept;
 
 #include <cassert>
 #include <memory>
 #include <mutex>
+#include <type_traits>
 
 #include "checking_mutex.h"
 
 int main(int, char**) {
-  checking_mutex m0;
-  checking_mutex m1;
-  std::unique_lock<checking_mutex> lk0(m0);
-  std::unique_lock<checking_mutex> lk1(m1);
-
-  auto& result = (lk1 = std::move(lk0));
-
-  assert(&result == &lk1);
-  assert(lk1.mutex() == std::addressof(m0));
-  assert(lk1.owns_lock());
-  assert(lk0.mutex() == nullptr);
-  assert(!lk0.owns_lock());
+  {
+    checking_mutex m0;
+    checking_mutex m1;
+    std::unique_lock<checking_mutex> lk0(m0);
+    std::unique_lock<checking_mutex> lk1(m1);
+
+    // Test self move assignment for lk0.
+    lk0 = std::move(lk0);
+    assert(lk0.mutex() == std::addressof(m0));
+    assert(lk0.owns_lock() == true);
+
+    auto& result = (lk1 = std::move(lk0));
+
+    assert(&result == &lk1);
+    assert(lk1.mutex() == std::addressof(m0));
+    assert(lk1.owns_lock());
+    assert(lk0.mutex() == nullptr);
+    assert(lk0.owns_lock() == false);
+
+    static_assert(std::is_nothrow_move_assignable<std::unique_lock<checking_mutex> >::value, "");
+  }
+
+  {
+    // Test self move-assignment (LWG4172)
+    checking_mutex m0;
+    std::unique_lock<checking_mutex> lk0(m0);
+    lk0 = std::move(lk0);
+    assert(lk0.mutex() == std::addressof(m0));
+    assert(lk0.owns_lock() == true);
+  }
 
   return 0;
 }


        


More information about the libcxx-commits mailing list