llvmorg-github-actions[bot] wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Jameson Nash (vtjnash)

<details>
<summary>Changes</summary>

Relands #<!-- -->231604, reverted in #<!-- -->233614 because it exposed 
ClangBuiltLinux/linux#<!-- -->2187, which #<!-- -->233965 has since fixed.

Conflicts resolved against #<!-- -->233965, which made the `&amp;S::m` wildcard 
project the field itself when `m` is a member of an anonymous struct or union 
in `S`, so it can match a held lock. That mapping is kept, now only under the 
pointer-to-member check. The C `&amp;um` test added there now names the same 
object's `um` rather than the wildcard.

This reverts commit 51ec77936fb5423bb713102a8aa35759fa83e116.

---
Full diff: https://github.com/llvm/llvm-project/pull/237302.diff


4 Files Affected:

- (modified) clang/lib/Analysis/ThreadSafetyCommon.cpp (+3-6) 
- (modified) clang/lib/Sema/SemaDeclAttr.cpp (+5-5) 
- (modified) clang/test/Sema/warn-thread-safety-analysis.c (+32-5) 
- (modified) clang/test/SemaCXX/warn-thread-safety-analysis.cpp (+20) 


``````````diff
diff --git a/clang/lib/Analysis/ThreadSafetyCommon.cpp 
b/clang/lib/Analysis/ThreadSafetyCommon.cpp
index c48ddd48c6f91..b3c7511e47f19 100644
--- a/clang/lib/Analysis/ThreadSafetyCommon.cpp
+++ b/clang/lib/Analysis/ThreadSafetyCommon.cpp
@@ -730,11 +730,9 @@ til::SExpr *SExprBuilder::translateUnaryOperator(const 
UnaryOperator *UO,
 
   case UO_AddrOf:
     if (CapabilityExprMode) {
-      // interpret &Graph::mu_ as an existential.
-      if (const auto *DRE = dyn_cast<DeclRefExpr>(UO->getSubExpr())) {
-        if (DRE->getDecl()->isCXXInstanceMember()) {
-          // This is a pointer-to-member expression, e.g. &MyClass::mu_.
-          // We interpret this syntax specially, as a wildcard.
+      // A pointer-to-member expression, &MyClass::mu_, is read as a wildcard.
+      if (UO->getType()->isMemberPointerType())
+        if (const auto *DRE = dyn_cast<DeclRefExpr>(UO->getSubExpr())) {
           // Name a member of an anonymous struct/union by its field, as a
           // projection of it does.
           const ValueDecl *D = DRE->getDecl();
@@ -743,7 +741,6 @@ til::SExpr *SExprBuilder::translateUnaryOperator(const 
UnaryOperator *UO,
           auto *W = new (Arena) til::Wildcard();
           return new (Arena) til::Project(W, D);
         }
-      }
     }
     // otherwise, & is a no-op
     return translate(UO->getSubExpr(), Ctx);
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 33b680c4d93d4..6205c70510159 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -395,13 +395,13 @@ static void checkAttrArgsAreCapabilityObjs(Sema &S, Decl 
*D,
 
     QualType ArgTy = ArgExp->getType();
 
-    // A pointer to member expression of the form  &MyClass::mu is treated
-    // specially -- we need to look at the type of the member.
+    // A pointer to member expression of the form &MyClass::mu is treated
+    // specially -- we need to look at the type of the member. The analysis
+    // reads it as that member of any object.
     if (const auto *UOp = dyn_cast<UnaryOperator>(ArgExp))
       if (UOp->getOpcode() == UO_AddrOf)
-        if (const auto *DRE = dyn_cast<DeclRefExpr>(UOp->getSubExpr()))
-          if (DRE->getDecl()->isCXXInstanceMember())
-            ArgTy = DRE->getDecl()->getType();
+        if (const auto *MPT = ArgTy->getAs<MemberPointerType>())
+          ArgTy = MPT->getPointeeType();
 
     // First see if we can just cast to record type, or pointer to record type.
     const auto *RD = getRecordDecl(ArgTy);
diff --git a/clang/test/Sema/warn-thread-safety-analysis.c 
b/clang/test/Sema/warn-thread-safety-analysis.c
index 37dd29a659e97..8339dcf8d859f 100644
--- a/clang/test/Sema/warn-thread-safety-analysis.c
+++ b/clang/test/Sema/warn-thread-safety-analysis.c
@@ -315,8 +315,34 @@ void test_fp_ops(struct FPOps *ops) {
 }
 
 void test_fp_ops_fail(struct FPOps *ops) {
-  ops->a = 42; // expected-warning {{writing variable 'a' requires holding 
mutex '&FPOps::mu' exclusively}}
-  ops->requires_mu(); // expected-warning {{calling function 'requires_mu' 
requires holding mutex '&FPOps::mu' exclusively}}
+  ops->a = 42; // expected-warning {{writing variable 'a' requires holding 
mutex 'ops->mu' exclusively}}
+  ops->requires_mu(); // expected-warning {{calling function 'requires_mu' 
requires holding mutex 'ops->mu' exclusively}}
+}
+
+// '&mu' names the mutex of the same object, as 'mu' does; C has no
+// pointer-to-member syntax, so it is not the wildcard '&FPOps::mu' is in C++.
+void test_fp_ops_other(struct FPOps *ops, struct FPOps *other) {
+  other->lock();
+  ops->a = 42; // expected-warning {{writing variable 'a' requires holding 
mutex 'ops->mu' exclusively}} \
+               // expected-note {{found near match 'other->mu'}}
+  ops->requires_mu(); // expected-warning {{calling function 'requires_mu' 
requires holding mutex 'ops->mu' exclusively}} \
+                      // expected-note {{found near match 'other->mu'}}
+  other->unlock();
+}
+
+// The object may be reached without a pointer, and the field may be called
+// through an explicit dereference.
+void test_fp_ops_value(struct FPOps foo, struct FPOps *ops) {
+  foo.lock();
+  foo.a = 42;
+  (*ops->lock)();
+  ops->a = 42;
+  (*foo.unlock)();
+  foo.a = 42; // expected-warning {{writing variable 'a' requires holding 
mutex 'foo.mu' exclusively}} \
+              // expected-note {{found near match 'ops->mu'}}
+  foo.requires_mu(); // expected-warning {{calling function 'requires_mu' 
requires holding mutex 'foo.mu' exclusively}} \
+                     // expected-note {{found near match 'ops->mu'}}
+  ops->unlock();
 }
 
 // An attribute on a member of an anonymous struct/union may name a field of
@@ -344,7 +370,7 @@ struct AnonMembers {
   int h GUARDED_BY(deep);
   union { struct Mutex um; };
   union { int i GUARDED_BY(um); };
-  int j GUARDED_BY(&um); // the wildcard, any object's um
+  int j GUARDED_BY(&um); // this object's um, as for i
   struct Mutex after ACQUIRED_AFTER(inner);
 };
 
@@ -367,10 +393,11 @@ void test_anon_members(struct AnonMembers *s, struct 
AnonMembers *other) {
   s->j = 10;
   mutex_unlock(&s->um);
   mutex_exclusive_lock(&other->um);
-  s->j = 10;
+  s->j = 10; // expected-warning {{writing variable 'j' requires holding mutex 
's->um' exclusively}} \
+             // expected-note {{found near match 'other->um'}}
   mutex_unlock(&other->um);
   mutex_exclusive_lock(&other->mu);
-  s->j = 10; // expected-warning {{writing variable 'j' requires holding mutex 
'&AnonMembers::um' exclusively}}
+  s->j = 10; // expected-warning {{writing variable 'j' requires holding mutex 
's->um' exclusively}}
   s->a = 1; // expected-warning {{writing variable 'a' requires holding mutex 
's->mu' exclusively}} \
             // expected-note {{found near match 'other->mu'}}
   s->e = 5; // expected-warning {{writing variable 'e' requires holding mutex 
's->inner' exclusively}}
diff --git a/clang/test/SemaCXX/warn-thread-safety-analysis.cpp 
b/clang/test/SemaCXX/warn-thread-safety-analysis.cpp
index 34c68679c3837..4face3fd43464 100644
--- a/clang/test/SemaCXX/warn-thread-safety-analysis.cpp
+++ b/clang/test/SemaCXX/warn-thread-safety-analysis.cpp
@@ -8482,6 +8482,26 @@ struct GuardedOpsSelf {
   }
 };
 
+// '&mu' is '&this->mu', the mutex of the called object, whereas the pointer
+// to member '&GuardedOpsAddrOf::mu' is the 'mu' of any object.
+struct GuardedOpsAddrOf {
+  Mutex mu;
+  int data GUARDED_BY(&mu);
+  int any GUARDED_BY(&GuardedOpsAddrOf::mu);
+  void (*lock)() EXCLUSIVE_LOCK_FUNCTION(&mu);
+  void (*unlock)() UNLOCK_FUNCTION(&mu);
+};
+
+void testMemberAddrOf(GuardedOpsAddrOf *g, GuardedOpsAddrOf *h) {
+  h->lock();
+  h->data = 1;
+  g->any = 1;
+  g->data = 1; // expected-warning {{writing variable 'data' requires holding 
mutex 'g->mu' exclusively}} \
+               // expected-note {{found near match 'h->mu'}}
+  h->unlock();
+  g->any = 1; // expected-warning {{writing variable 'any' requires holding 
mutex '&FunctionPointers::GuardedOpsAddrOf::mu' exclusively}}
+}
+
 } // namespace FunctionPointers
 
 namespace AnonymousMembers {

``````````

</details>


https://github.com/llvm/llvm-project/pull/237302
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to