Strange "release_shared_capability" behavior in Clang thread safety analysis

While experimenting with Clang’s thread safety analysis ( I ran into a compiler warning I didn’t understand. It seems that the release_shared_capability attribute is not doing what I expect. The code to reproduce is below:

// Begin file:
// Fake mutex class.

class attribute((capability(“mutex”))) mutex {
void SharedLock() attribute((acquire_shared_capability())) {
// Do nothing for this fake mutex.
void SharedUnlock() attribute((release_shared_capability())) {
// Do nothing for this fake mutex.

// Scoped lock class to do RAII locking for the fake mutex class above.
class attribute((scoped_lockable)) mutex_lock {
mutex_lock(mutex *Mutex) attribute((acquire_shared_capability(Mutex)))
: Mutex(Mutex) {
~mutex_lock() attribute((release_shared_capability())) {

mutex *Mutex;

// Global mutex and a corresponding value for it to protect.
mutex Mutex;
int Value attribute((guarded_by(Mutex))) = 42;

// Explicitly lock and unlock the mutex to read the value.
int getValueExplicitUnlock() {
int LocalValue = Value;
return LocalValue;

// Use the scoped lock class to lock and unlock the mutex.
// This is where the problem happens.
int getValueScopedLock() {
mutex_lock Lock(&Mutex);
return Value;

// End file:

Compiling with tip-of-tree clang with the -Wthread-safety flag, I get the following warning:

Begin shell session

$ clang++ -Wthread-safety -c warning: releasing mutex ‘Lock’ using shared access, expected exclusive access
return Value;
1 warning generated.

End shell session

So there is a problem with my scoped lock in the getValueScopedLock function, but there is no problem with the explicit locking and unlocking in my getValueExplicitUnlock function.

The warning goes away if I switch the annotation on the ~mutex_lock destructor to “release_capability” instead of “release_shared_capability”. Is this the expected behavior? It seems like an “acquire_shared_capability” should be paired with a “release_shared_capability”.

I also found a stack overflow question where someone else ran into this problem:

For reference, this is Thread Safety Analysis [-Wthread-safety-analysis] work wrong with shared lock · Issue #32851 · llvm/llvm-project · GitHub, which I closed as invalid because I think it was intended to work like this. This is also documented now.