<table border="1" cellspacing="0" cellpadding="8">
    <tr>
        <th>Issue</th>
        <td>
            <a href=https://github.com/llvm/llvm-project/issues/63239>63239</a>
        </td>
    </tr>

    <tr>
        <th>Summary</th>
        <td>
            Thread safety warning when conditionally releasing a `unique_lock`
        </td>
    </tr>

    <tr>
      <th>Labels</th>
      <td>
            new issue
      </td>
    </tr>

    <tr>
      <th>Assignees</th>
      <td>
      </td>
    </tr>

    <tr>
      <th>Reporter</th>
      <td>
          greg7mdp
      </td>
    </tr>
</table>

<pre>
    When conditionally releasing a `unique_lock`, the unique_lock destructor warns with:

 `mutex 'm' is not held on every path through here [-Wthread-safety-analysis]`

however hitting the destructor with an unlocked mutex is expected with `unique_lock` which supports `unlock()`. 

In the doc, it says explicitly:

```c++
  // Release *this and all associated mutexes, if they are still held.
  // There is no warning if the scope was already unlocked before.
  ~MutexLocker() RELEASE() {
    if (locked)
 mut->GenericUnlock();
  }
```

but I do get a warning if the scope was unlocked before.

Here is a self-contained program reproducing the issue when compiled with `clang++-17`:

```
// compile with: clang++-17 -Wthread-safety tsa.cpp
// warning:
// tsa.cpp:119:1: warning: mutex 'm' is not held on every path through here [-Wthread-safety-analysis]
#if defined(__clang__) && (!defined(SWIG))
#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
#else
#define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op
#endif

#define CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(capability(x))

#define SCOPED_CAPABILITY THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)

#define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))

#define PT_GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))

#define ACQUIRED_BEFORE(...) THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__))

#define ACQUIRED_AFTER(...) THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__))

#define REQUIRES(...) THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__))

#define REQUIRES_SHARED(...) THREAD_ANNOTATION_ATTRIBUTE__(requires_shared_capability(__VA_ARGS__))

#define ACQUIRE(...) THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__))

#define ACQUIRE_SHARED(...) THREAD_ANNOTATION_ATTRIBUTE__(acquire_shared_capability(__VA_ARGS__))

#define RELEASE(...) THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__))

#define RELEASE_SHARED(...) THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__))

#define RELEASE_GENERIC(...) THREAD_ANNOTATION_ATTRIBUTE__(release_generic_capability(__VA_ARGS__))

#define TRY_ACQUIRE(...) THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_capability(__VA_ARGS__))

#define TRY_ACQUIRE_SHARED(...) THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(__VA_ARGS__))

#define EXCLUDES(...) THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))

#define ASSERT_CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(assert_capability(x))

#define ASSERT_SHARED_CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_capability(x))

#define RETURN_CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))

#define NO_THREAD_SAFETY_ANALYSIS THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)

#include <mutex>
#include <shared_mutex>

// Defines an annotated interface for mutexes.
// These methods can be implemented to use any internal mutex implementation.
class CAPABILITY("mutex") mutex {
private:
 std::mutex mutex_;

public:
   // Acquire/lock this mutex exclusively.  Only one thread can have exclusive
   // access at any one time.  Write operations to guarded data require an
   // exclusive lock.
   void lock() ACQUIRE() { mutex_.lock(); }

   // Release/unlock an exclusive mutex.
   void unlock() RELEASE() { mutex_.unlock(); }

   // Try to acquire the mutex.  Returns true on success, and false on failure.
   bool try_lock() TRY_ACQUIRE(true) { return mutex_.try_lock(); }
};

// Tag types for selecting a constructor.
struct adopt_lock_t {} inline constexpr adopt_lock = {};
struct defer_lock_t {} inline constexpr defer_lock = {};
struct shared_lock_t {} inline constexpr shared_lock = {};
struct try_to_lock_t {} inline constexpr try_to_lock = {};

// unique_lock is an RAII class that acquires a mutex in its constructor, and
// releases it in its destructor.
template <typename M>
class SCOPED_CAPABILITY unique_lock {
private:
   M*   mut;
   bool locked;

public:
 unique_lock() noexcept
      : mut(nullptr)
      , locked(false) {}

   // Assume mu is held, implicitly acquire *this and associate it with mu.
   unique_lock(M& mu, adopt_lock_t) REQUIRES(mu)
      : mut(&mu)
      , locked(true) {}

   // Release *this and all associated mutexes, if they are still held.
   // There is no warning if the scope was already unlocked before.
   ~unique_lock() RELEASE() {
 if (locked)
         mut->unlock();
   }

   // Acquire all associated mutexes exclusively.
   void lock() ACQUIRE() {
 mut->lock();
      locked = true;
   }

   // Release all associated mutexes. Warn on double unlock.
   void unlock() RELEASE() {
      mut->unlock();
      locked = false;
   }
};

mutex m;

void release_mutex(int i) RELEASE(m) {
   unique_lock l(m, adopt_lock);
   if (i)
      l.unlock();
}

int main() {
 m.lock();
   release_mutex(1);
   return 0;
}
```
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJy0WU1z4rrS_jXOpisUEQmBBQsnkBmqZpJ5gbxzZ-USdoN1ryz5SHISNue339KHwXwkQHJuKkUKW3qe7qellrpDtWZLgTiIbu6im-EFrUwu1WCpcHlbZOXFXGarwe8cBaRSZMwwKSjnK1DIkWomlkAh6rYrwf6qMOEy_U_UbUfkHkyO0HgKGWqjqtRIBa9UCQ2vzORRJ47aw6gdPi1SURl8g4jcFhG5BaZBSAM58gykAHxBtYKSmhxMrmS1zCFHhRDd3F3-NrlCml1qukCzuqSC8pVmOroZWosaLLl8tTiQM2OsA9bSpnXM5EAFVMLajRl4i5gGfCsxNZj5IXtew2vO0hx0VZZSGe0HuHekF5F-1G23oGnHWHhqmVq9mAFNV46Es5QZvtoRx3rhftOI3NlfLxlE5CEiDzBxAUGISGxypoGKDCjnQLWWKaOmdgS1Y1tY7hVQhaAN49xJ3NrBnDlxXQxc0KxafiboVJYIr1QD5Vb21UavOS6kwjXW3z8t7Q_7TnklYDL6MYqno_Atul37AhY-Ij2PZEXzL4rKXEad0TcUqFj63FS1sxHidrijVFO-eWVgDJmEJRqg77tz2A3_-T3IQUEjX1ymUhjKBGZQKrlUtACFpZJZldbLimldIbz67VOUjDcWT8qpWPpQXl7dWmvfiXf46kMSYOrdA9sosLMLwGjaSstyCyK4vqHzj-uhnfjqqm8_LfxmLPwvNmbg77AFZLiwUkaklyTOqSRxi4N0I9IFF-yrzZjp7_E3G_16hUSk41_C7PtkFA-T-PHxaRbPxk-PSTybTcZ3z7ORRey9WdQkocYoNq8MuofheRMOucZPYQc5hbyU5QZNZGyxFd016H38K74b_xjP_tQIx2hSWtI548ys9uzewZ7eP_0aDZMNxVFstxEyl9LonOO7yN-e48lwNEzuTrZ6WVGVYZbMj1r9a5acD1-a5HSG-P7_nscTiz96eJrYRNRqtU5hoelfFVOWw2UHt1r_P07iybepW6_HGeOH2WjyCUK6MC5_nsg3GTm-6elMCh2TTraW15l0yfR7PBkNP8Gqc2rd_Ax50PZsUb9CdrajNecX_NwcmyeL6-4EXyH7REA959f9TL6NHkeT8f353Et_T_gU-WzyJzl7QRm1Sr6yqBqkZwve5P6C6KN_3f94Hp6TMOwZoRN8S3mVhYP7tB00nY4ms-T8c49qjcokpx9_gckr-nnCQ6p-yDsZzZ4nj58gtJImCk2l_DXnQ5bHpyTgTeOH0exPEj_GP_5Mx9OjNEIm_k6W-DtZsr6T7XEx4cILUefeXQCjzujQuyDRzpDm9XLojLZ1CVAhpHEVCRMG1YKmCAup6vqktTVvlqNGKNDkMtOQUgFzBFaUHAsUFsNIqDQCFSsPJyivS7Z6FLVla4BNOdV6-9IVEeINJ8QGKVx066qkVOyFGlxfl0GbzH7pxH6g-0zWdUiYVM05Szdz1iVV7HdqRB5cVexKNY_jNpJmL8hXLYAnwVcgBYIPlHM8py-4GbaLTNMUtQZqnBRuKiuwBfBbMYMgS1ROB20VC1clyKihEM5goGIXc00G1tp1SQcvkmWwKcIaJ3Ao6IIqra1CrVGh7fCE4jUiD778sqtkw-2wdsiblfV-PVnTV-JEA2ZqZWUJedTVbp4VYOK2owajKrSljq6c0LaKtjX2gnLtni8o41Wj7oW5lBxsdm5Yun26WMjaYr_ra8O3p22bfjvcWWy1D3QJZlWidptJI8fU-AZNKkXd3gjm-a9AM1kaR5QYt-Jvh8AEt9nFzcG3UjUGQdQZhmFrEwJShgtUx5A2gz5ACqnkCFRj1AdYVkYjj2E1Rh3C2hK52dJyXRaYxOMx-Kxicrv7_BrSQOs0JIAZ3QxCWDtbwOHuooGZesamKRWiZrAoOTUu49pIC1og_FznW2_Efs3XtPn9tAbwMyL2T1GZRlfFL-PQkPk4yTXbYX65C4lvKZZm098BCL0EewxVnJdGbdo8_j25r-lIz-2uukn03uaNta4Ku2FtRHLkmetwFXUfbb2rt1pjdVvM6u1aMkW12brbnvyMSBeKykWtsWF85lmXWXZA_7CjEenuv2262UgEx3LkP9Pg-0c7fPD3fuTfa_Ed7u_VP6HPVx1u8L2fvsOp-o4eW0fr6WfYdu_xsEUAIYgucbgwnmJvHcvD9rbgN1XCnimZrOYcg_BnnYBNC4-ouu2D33EHndjLieECtPPUWVeXYuFi1WPCANu2tNg1tZmnuB_Q3HE7ZvuVxHYWEd898ze2N0y01hSUif1gtw5rtOvO1d57d3q3DxDWbdyLbNDJ-p0-vcDBVbfXvSbX7Xb3Ih-QG9ruY3fevk57bdIh14TepDd4fd2mPUL76QUbkDbptLtX7at-u3vdbWX9215GCOl22_NORtLouo0FZbzF-UvRkmp54RrPg26HdPoXnM6Ra_cPHkIEvvqutL3s3gwv1MDOuZxXSx1dtznTRm9QDDMcBzN_Aw0d5TpLvJ75P6GLSvFBbkyp7YHhtsGSmbyat1JZ2Oswf6n_XJZK_htTE5EHZ6qOyINz5b8BAAD__woi-xc">