[llvm-branch-commits] Thread Safety Analysis: Add guarded_by_any and pt_guarded_by_any (PR #185173)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Sat Mar 7 04:09:29 PST 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Marco Elver (melver)
<details>
<summary>Changes</summary>
Introduce two new attributes with a weaker ownership model than
guarded_by: writing requires all listed capabilities to be held
exclusively, while reading only requires at least one to be held.
This is sound because any writer must hold all capabilities, so
holding any one of them guarantees at least shared (read) access.
This synchronization pattern is frequently used where the underlying
lock implementation does not support real reader locking, and instead
several lock "shards" are used to reduce contention for readers. For
example, the Linux kernel makes frequent use of this pattern [1].
[1] https://lore.kernel.org/all/20250307085204.GJ16878@<!-- -->noisy.programming.kicks-ass.net/
---
Patch is 21.91 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/185173.diff
13 Files Affected:
- (modified) clang/docs/ReleaseNotes.rst (+6)
- (modified) clang/docs/ThreadSafetyAnalysis.rst (+41)
- (modified) clang/include/clang/Analysis/Analyses/ThreadSafety.h (+11)
- (modified) clang/include/clang/Basic/Attr.td (+22)
- (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+6)
- (modified) clang/lib/AST/ASTImporter.cpp (+14)
- (modified) clang/lib/Analysis/ThreadSafety.cpp (+49)
- (modified) clang/lib/Sema/AnalysisBasedWarnings.cpp (+27)
- (modified) clang/lib/Sema/SemaDeclAttr.cpp (+27)
- (modified) clang/lib/Sema/SemaDeclCXX.cpp (+4)
- (modified) clang/test/SemaCXX/thread-safety-annotations.h (+2)
- (modified) clang/test/SemaCXX/warn-thread-safety-analysis.cpp (+44)
- (modified) clang/test/SemaCXX/warn-thread-safety-parsing.cpp (+62-3)
``````````diff
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index eb4cc616897b4..8a14fd0a818d2 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -213,6 +213,12 @@ Attribute Changes in Clang
and requires all listed capabilities to be held when accessing the guarded
variable.
+- The :doc:`ThreadSafetyAnalysis` introduces two new attributes,
+ ``guarded_by_any`` and ``pt_guarded_by_any``. Unlike ``guarded_by``, these
+ follow a weaker ownership model: *writing* still requires all listed
+ capabilities to be held exclusively, but *reading* only requires at least one
+ of them to be held.
+
Improvements to Clang's diagnostics
-----------------------------------
- Added ``-Wlifetime-safety`` to enable lifetime safety analysis,
diff --git a/clang/docs/ThreadSafetyAnalysis.rst b/clang/docs/ThreadSafetyAnalysis.rst
index 0628206918f7f..3b45151e648c3 100644
--- a/clang/docs/ThreadSafetyAnalysis.rst
+++ b/clang/docs/ThreadSafetyAnalysis.rst
@@ -206,6 +206,41 @@ When multiple capabilities are listed, all of them must be held:
}
+GUARDED_BY_ANY(...) and PT_GUARDED_BY_ANY(...)
+----------------------------------------------
+
+``GUARDED_BY_ANY`` is an attribute on data members that declares the data
+member is protected by a set of capabilities, exploiting the following
+invariant: a writer must hold *all* listed capabilities exclusively, so
+holding *any one* of them is sufficient to guarantee at least shared (read)
+access. Concretely:
+
+* **Write** access requires *all* listed capabilities to be held exclusively.
+* **Read** access requires *at least one* of the listed capabilities to be held
+ (shared or exclusive).
+
+``PT_GUARDED_BY_ANY`` is the pointer counterpart: the data member itself is
+unconstrained, but the *data it points to* follows the same rules.
+
+.. code-block:: c++
+
+ Mutex mu1, mu2;
+ int a GUARDED_BY_ANY(mu1, mu2);
+
+ void reader() REQUIRES_SHARED(mu1) {
+ int x = a; // OK: mu1 is held (shared read access).
+ a = 1; // Warning! Writing requires both mu1 and mu2.
+ }
+
+ void writer() REQUIRES(mu1, mu2) {
+ a = 1; // OK: both mu1 and mu2 are held exclusively.
+ }
+
+ void unprotected() {
+ int x = a; // Warning! No capability held at all.
+ }
+
+
REQUIRES(...), REQUIRES_SHARED(...)
-----------------------------------
@@ -890,6 +925,12 @@ implementation.
#define PT_GUARDED_BY(...) \
THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(__VA_ARGS__))
+ #define GUARDED_BY_ANY(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(guarded_by_any(__VA_ARGS__))
+
+ #define PT_GUARDED_BY_ANY(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by_any(__VA_ARGS__))
+
#define ACQUIRED_BEFORE(...) \
THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__))
diff --git a/clang/include/clang/Analysis/Analyses/ThreadSafety.h b/clang/include/clang/Analysis/Analyses/ThreadSafety.h
index 9fb23212aaf97..2b7c20553c578 100644
--- a/clang/include/clang/Analysis/Analyses/ThreadSafety.h
+++ b/clang/include/clang/Analysis/Analyses/ThreadSafety.h
@@ -193,6 +193,17 @@ class ThreadSafetyHandler {
virtual void handleNoMutexHeld(const NamedDecl *D, ProtectedOperationKind POK,
AccessKind AK, SourceLocation Loc) {}
+ /// Warn when a read of a guarded_by_any variable occurs while none of the
+ /// listed capabilities are held.
+ /// \param D -- The decl for the protected variable
+ /// \param POK -- The kind of protected operation (e.g. variable access)
+ /// \param LockNames -- Comma-separated list of capability names, quoted
+ /// \param Loc -- The location of the read
+ virtual void handleGuardedByAnyReadNotHeld(const NamedDecl *D,
+ ProtectedOperationKind POK,
+ StringRef LockNames,
+ SourceLocation Loc) {}
+
/// Warn when a protected operation occurs while the specific mutex protecting
/// the operation is not locked.
/// \param Kind -- the capability's name parameter (role, mutex, etc).
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index c3068ce9e69d9..ee9887ebe268b 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -4175,6 +4175,28 @@ def PtGuardedBy : InheritableAttr {
let Documentation = [Undocumented];
}
+def GuardedByAny : InheritableAttr {
+ let Spellings = [GNU<"guarded_by_any">];
+ let Args = [VariadicExprArgument<"Args">];
+ let LateParsed = LateAttrParseExperimentalExt;
+ let TemplateDependent = 1;
+ let ParseArgumentsAsUnevaluated = 1;
+ let InheritEvenIfAlreadyPresent = 1;
+ let Subjects = SubjectList<[Field, SharedVar]>;
+ let Documentation = [Undocumented];
+}
+
+def PtGuardedByAny : InheritableAttr {
+ let Spellings = [GNU<"pt_guarded_by_any">];
+ let Args = [VariadicExprArgument<"Args">];
+ let LateParsed = LateAttrParseExperimentalExt;
+ let TemplateDependent = 1;
+ let ParseArgumentsAsUnevaluated = 1;
+ let InheritEvenIfAlreadyPresent = 1;
+ let Subjects = SubjectList<[Field, SharedVar]>;
+ let Documentation = [Undocumented];
+}
+
def AcquiredAfter : InheritableAttr {
let Spellings = [GNU<"acquired_after">];
let Args = [VariadicExprArgument<"Args">];
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 0e6b3f51a5231..c6d2f27994fd3 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -4328,6 +4328,12 @@ def warn_var_deref_requires_any_lock : Warning<
"%select{reading|writing}1 the value pointed to by %0 requires holding "
"%select{any mutex|any mutex exclusively}1">,
InGroup<ThreadSafetyAnalysis>, DefaultIgnore;
+def warn_variable_requires_any_of_locks : Warning<
+ "reading variable %0 requires holding at least one of %1">,
+ InGroup<ThreadSafetyAnalysis>, DefaultIgnore;
+def warn_var_deref_requires_any_of_locks : Warning<
+ "reading the value pointed to by %0 requires holding at least one of %1">,
+ InGroup<ThreadSafetyAnalysis>, DefaultIgnore;
def warn_fun_excludes_mutex : Warning<
"cannot call function '%1' while %0 '%2' is held">,
InGroup<ThreadSafetyAnalysis>, DefaultIgnore;
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index da2bda1b43526..690bf714cec60 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -9761,6 +9761,20 @@ Expected<Attr *> ASTImporter::Import(const Attr *FromAttr) {
From->args_size());
break;
}
+ case attr::GuardedByAny: {
+ const auto *From = cast<GuardedByAnyAttr>(FromAttr);
+ AI.importAttr(From,
+ AI.importArrayArg(From->args(), From->args_size()).value(),
+ From->args_size());
+ break;
+ }
+ case attr::PtGuardedByAny: {
+ const auto *From = cast<PtGuardedByAnyAttr>(FromAttr);
+ AI.importAttr(From,
+ AI.importArrayArg(From->args(), From->args_size()).value(),
+ From->args_size());
+ break;
+ }
case attr::AcquiredAfter: {
const auto *From = cast<AcquiredAfterAttr>(FromAttr);
AI.importAttr(From,
diff --git a/clang/lib/Analysis/ThreadSafety.cpp b/clang/lib/Analysis/ThreadSafety.cpp
index 91017178fea25..b5c28ce955d36 100644
--- a/clang/lib/Analysis/ThreadSafety.cpp
+++ b/clang/lib/Analysis/ThreadSafety.cpp
@@ -41,6 +41,7 @@
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/Casting.h"
@@ -1282,6 +1283,12 @@ class ThreadSafetyAnalyzer {
void warnIfMutexHeld(const FactSet &FSet, const NamedDecl *D, const Expr *Exp,
Expr *MutexExp, til::SExpr *Self, SourceLocation Loc);
+ void warnIfAnyMutexNotHeldForRead(const FactSet &FSet, const NamedDecl *D,
+ const Expr *Exp,
+ llvm::ArrayRef<Expr *> Args,
+ ProtectedOperationKind POK,
+ SourceLocation Loc);
+
void checkAccess(const FactSet &FSet, const Expr *Exp, AccessKind AK,
ProtectedOperationKind POK);
void checkPtAccess(const FactSet &FSet, const Expr *Exp, AccessKind AK,
@@ -1868,6 +1875,24 @@ void ThreadSafetyAnalyzer::warnIfMutexHeld(const FactSet &FSet,
}
}
+void ThreadSafetyAnalyzer::warnIfAnyMutexNotHeldForRead(
+ const FactSet &FSet, const NamedDecl *D, const Expr *Exp,
+ llvm::ArrayRef<Expr *> Args, ProtectedOperationKind POK,
+ SourceLocation Loc) {
+ SmallVector<std::string, 2> Names;
+ for (auto *Arg : Args) {
+ CapabilityExpr Cp = SxBuilder.translateAttrExpr(Arg, D, Exp, nullptr);
+ if (Cp.isInvalid() || Cp.shouldIgnore())
+ continue;
+ const FactEntry *LDat = FSet.findLockUniv(FactMan, Cp);
+ if (LDat && LDat->isAtLeast(LK_Shared))
+ return; // At least one held — read access is safe.
+ Names.push_back("'" + Cp.toString() + "'");
+ }
+ if (!Names.empty())
+ Handler.handleGuardedByAnyReadNotHeld(D, POK, llvm::join(Names, ", "), Loc);
+}
+
/// Checks guarded_by and pt_guarded_by attributes.
/// Whenever we identify an access (read or write) to a DeclRefExpr that is
/// marked with guarded_by, we must ensure the appropriate mutexes are held.
@@ -1937,6 +1962,17 @@ void ThreadSafetyAnalyzer::checkAccess(const FactSet &FSet, const Expr *Exp,
for (const auto *I : D->specific_attrs<GuardedByAttr>())
for (auto *Arg : I->args())
warnIfMutexNotHeld(FSet, D, Exp, AK, Arg, POK, nullptr, Loc);
+
+ for (const auto *I : D->specific_attrs<GuardedByAnyAttr>()) {
+ if (AK == AK_Written) {
+ // Write requires all capabilities.
+ for (auto *Arg : I->args())
+ warnIfMutexNotHeld(FSet, D, Exp, AK, Arg, POK, nullptr, Loc);
+ } else {
+ // Read requires at least one capability.
+ warnIfAnyMutexNotHeldForRead(FSet, D, Exp, I->args(), POK, Loc);
+ }
+ }
}
/// Checks pt_guarded_by and pt_guarded_var attributes.
@@ -2003,6 +2039,19 @@ void ThreadSafetyAnalyzer::checkPtAccess(const FactSet &FSet, const Expr *Exp,
for (auto *Arg : I->args())
warnIfMutexNotHeld(FSet, D, Exp, AK, Arg, PtPOK, nullptr,
Exp->getExprLoc());
+
+ for (const auto *I : D->specific_attrs<PtGuardedByAnyAttr>()) {
+ if (AK == AK_Written) {
+ // Write requires all capabilities.
+ for (auto *Arg : I->args())
+ warnIfMutexNotHeld(FSet, D, Exp, AK, Arg, PtPOK, nullptr,
+ Exp->getExprLoc());
+ } else {
+ // Read requires at least one capability.
+ warnIfAnyMutexNotHeldForRead(FSet, D, Exp, I->args(), PtPOK,
+ Exp->getExprLoc());
+ }
+ }
}
/// Process a function call, method call, constructor call,
diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp
index ea5ac639ffd5e..5ade30ea56528 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -2141,6 +2141,33 @@ class ThreadSafetyReporter : public clang::threadSafety::ThreadSafetyHandler {
Warnings.emplace_back(std::move(Warning), getNotes());
}
+ void handleGuardedByAnyReadNotHeld(const NamedDecl *D,
+ ProtectedOperationKind POK,
+ StringRef LockNames,
+ SourceLocation Loc) override {
+ unsigned DiagID = 0;
+ switch (POK) {
+ case POK_VarAccess:
+ case POK_PassByRef:
+ case POK_ReturnByRef:
+ case POK_PassPointer:
+ case POK_ReturnPointer:
+ DiagID = diag::warn_variable_requires_any_of_locks;
+ break;
+ case POK_VarDereference:
+ case POK_PtPassByRef:
+ case POK_PtReturnByRef:
+ case POK_PtPassPointer:
+ case POK_PtReturnPointer:
+ DiagID = diag::warn_var_deref_requires_any_of_locks;
+ break;
+ case POK_FunctionCall:
+ llvm_unreachable("POK_FunctionCall not applicable here");
+ }
+ PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) << D << LockNames);
+ Warnings.emplace_back(std::move(Warning), getNotes());
+ }
+
void handleMutexNotHeld(StringRef Kind, const NamedDecl *D,
ProtectedOperationKind POK, Name LockName,
LockKind LK, SourceLocation Loc,
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 7380f0057f2fa..e95638be52df6 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -488,6 +488,27 @@ static void handlePtGuardedByAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
PtGuardedByAttr(S.Context, AL, Args.data(), Args.size()));
}
+static void handleGuardedByAnyAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
+ SmallVector<Expr *, 1> Args;
+ if (!checkGuardedByAttrCommon(S, D, AL, Args))
+ return;
+
+ D->addAttr(::new (S.Context)
+ GuardedByAnyAttr(S.Context, AL, Args.data(), Args.size()));
+}
+
+static void handlePtGuardedByAnyAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
+ SmallVector<Expr *, 1> Args;
+ if (!checkGuardedByAttrCommon(S, D, AL, Args))
+ return;
+
+ if (!threadSafetyCheckIsPointer(S, D, AL))
+ return;
+
+ D->addAttr(::new (S.Context)
+ PtGuardedByAnyAttr(S.Context, AL, Args.data(), Args.size()));
+}
+
static bool checkAcquireOrderAttrCommon(Sema &S, Decl *D, const ParsedAttr &AL,
SmallVectorImpl<Expr *> &Args) {
if (!AL.checkAtLeastNumArgs(S, 1))
@@ -7988,6 +8009,12 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
case ParsedAttr::AT_PtGuardedBy:
handlePtGuardedByAttr(S, D, AL);
break;
+ case ParsedAttr::AT_GuardedByAny:
+ handleGuardedByAnyAttr(S, D, AL);
+ break;
+ case ParsedAttr::AT_PtGuardedByAny:
+ handlePtGuardedByAnyAttr(S, D, AL);
+ break;
case ParsedAttr::AT_LockReturned:
handleLockReturnedAttr(S, D, AL);
break;
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index cfb78ef5f5897..76949e7fb7a34 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -19453,6 +19453,10 @@ bool Sema::checkThisInStaticMemberFunctionAttributes(CXXMethodDecl *Method) {
Args = llvm::ArrayRef(G->args_begin(), G->args_size());
else if (const auto *G = dyn_cast<PtGuardedByAttr>(A))
Args = llvm::ArrayRef(G->args_begin(), G->args_size());
+ else if (const auto *G = dyn_cast<GuardedByAnyAttr>(A))
+ Args = llvm::ArrayRef(G->args_begin(), G->args_size());
+ else if (const auto *G = dyn_cast<PtGuardedByAnyAttr>(A))
+ Args = llvm::ArrayRef(G->args_begin(), G->args_size());
else if (const auto *AA = dyn_cast<AcquiredAfterAttr>(A))
Args = llvm::ArrayRef(AA->args_begin(), AA->args_size());
else if (const auto *AB = dyn_cast<AcquiredBeforeAttr>(A))
diff --git a/clang/test/SemaCXX/thread-safety-annotations.h b/clang/test/SemaCXX/thread-safety-annotations.h
index bcda81cc69049..1678d9f1c58e7 100644
--- a/clang/test/SemaCXX/thread-safety-annotations.h
+++ b/clang/test/SemaCXX/thread-safety-annotations.h
@@ -32,7 +32,9 @@
#define EXCLUSIVE_UNLOCK_FUNCTION(...) __attribute__((release_capability(__VA_ARGS__)))
#define SHARED_UNLOCK_FUNCTION(...) __attribute__((release_shared_capability(__VA_ARGS__)))
#define GUARDED_BY(...) __attribute__((guarded_by(__VA_ARGS__)))
+#define GUARDED_BY_ANY(...) __attribute__((guarded_by_any(__VA_ARGS__)))
#define PT_GUARDED_BY(...) __attribute__((pt_guarded_by(__VA_ARGS__)))
+#define PT_GUARDED_BY_ANY(...) __attribute__((pt_guarded_by_any(__VA_ARGS__)))
// Common
#define REENTRANT_CAPABILITY __attribute__((reentrant_capability))
diff --git a/clang/test/SemaCXX/warn-thread-safety-analysis.cpp b/clang/test/SemaCXX/warn-thread-safety-analysis.cpp
index 9ef6d7d3349d2..57f1955f10bfd 100644
--- a/clang/test/SemaCXX/warn-thread-safety-analysis.cpp
+++ b/clang/test/SemaCXX/warn-thread-safety-analysis.cpp
@@ -1570,6 +1570,50 @@ void func()
}
} // end namespace thread_annot_lock_42
+namespace guarded_by_any {
+// Test GUARDED_BY_ANY: any one capability suffices for read; all are required
+// for write.
+class Foo {
+ Mutex mu1, mu2;
+ int x GUARDED_BY_ANY(mu1, mu2);
+ int *p PT_GUARDED_BY_ANY(mu1, mu2);
+
+ // All locks held exclusively: read and write both OK.
+ void f1() EXCLUSIVE_LOCKS_REQUIRED(mu1, mu2) {
+ x = 1;
+ *p = 1;
+ }
+
+ // Only mu1 held exclusively: read OK, write warns about mu2.
+ void f2() EXCLUSIVE_LOCKS_REQUIRED(mu1) {
+ int y = x;
+ int z = *p;
+ x = 1; // expected-warning {{writing variable 'x' requires holding mutex 'mu2' exclusively}}
+ *p = 1; // expected-warning {{writing the value pointed to by 'p' requires holding mutex 'mu2' exclusively}}
+ }
+
+ // One lock held shared: read OK, write warns about both.
+ void f3() SHARED_LOCKS_REQUIRED(mu1) {
+ int y = x;
+ int z = *p;
+ x = 1; // expected-warning {{writing variable 'x' requires holding mutex 'mu1' exclusively}} \
+ // expected-warning {{writing variable 'x' requires holding mutex 'mu2' exclusively}}
+ *p = 1; // expected-warning {{writing the value pointed to by 'p' requires holding mutex 'mu1' exclusively}} \
+ // expected-warning {{writing the value pointed to by 'p' requires holding mutex 'mu2' exclusively}}
+ }
+
+ // No locks held: read and write both warn.
+ void f4() {
+ int y = x; // expected-warning {{reading variable 'x' requires holding at least one of 'mu1', 'mu2'}}
+ int z = *p; // expected-warning {{reading the value pointed to by 'p' requires holding at least one of 'mu1', 'mu2'}}
+ x = 1; // expected-warning {{writing variable 'x' requires holding mutex 'mu1' exclusively}} \
+ // expected-warning {{writing variable 'x' requires holding mutex 'mu2' exclusively}}
+ *p = 1; // expected-warning {{writing the value pointed to by 'p' requires holding mutex 'mu1' exclusively}} \
+ // expected-warning {{writing the value pointed to by 'p' requires holding mutex 'mu2' exclusively}}
+ }
+};
+} // end namespace guarded_by_any
+
namespace thread_annot_lock_46 {
// Test the support for annotations on virtual functions.
class Base {
diff --git a/clang/test/SemaCXX/warn-thread-safety-parsing.cpp b/clang/test/SemaCXX/warn-thread-safety-parsing.cpp
index 86d998711a1a0..3fe94958b0443 100644
--- a/clang/test/SemaCXX/warn-thread-safety-parsing.cpp
+++ b/clang/test/SemaCXX/warn-thread-safety-parsing.cpp
@@ -5,9 +5,11 @@
#define LOCKABLE __attribute__ ((lockable))
#define REENTRANT_CAPABILITY __attribute__ ((reentrant_capability))
#define SCOPED_LOCKABLE __attribute__ ((scoped_lockable))
-#define GUARDED_BY(...) __attribute__ ((guarded_by(__VA_ARGS__)))
-#define GUARDED_VAR __attribute__ ((guarded_var))
-#define PT_GUARDED_BY(...) __attribute__ ((pt_guarded_by(__VA_ARGS__)))
+#define GUARDED_BY(...) __attribute__ ((guarded_by(__VA_ARGS__)))
+#define GUARDED_BY_ANY(...) __attribute__ ((guarded_by_any(__VA_ARGS__)))
+#define GUARDED_VAR __attribute__ ((guarded_var))
+#define PT_GUARDED_BY(...) __attribute__ ((pt_guarded_by(__VA_ARGS__)))
+#define PT_GUARDED_BY_ANY(...) __attribute__ ((pt_guarded_by_any(__VA_ARGS__)))
#define PT_GUARDED_VAR __attribute__ ((pt_guarded_var))
#define ACQUIRED_AFTER(...) __attribute__ ((acquired_after(__VA_ARGS__)))
#define ACQUIRED_BEFORE(...) __attribute__ ((acquired_before(__VA_ARGS__)))
@@ -454,6 +456,63 @@ int * pgb_var_arg_bad_4 PT_GUARDED_BY(umu); // \
// expected-warning {{'pt_guarded_by' attribute requires arguments whose type is annotated with 'capability' attribute}}
+//-----------------------------------------//
+// Guarded By Any Attribute (gba)
+//-----------------------------------------//
+
+#if !__has_attribute(guarded_by_any)
+#error "Should support guarded_by_any attribute"
+#endif
+
+int gba_var_arg GUARDED_BY_ANY(mu1);
+int gba_var_args GUARDED_BY_ANY(mu1, mu2);
+
+int gba_var_noargs __attribute__((guard...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/185173
More information about the llvm-branch-commits
mailing list