Skip to content

Support [[guarded_by(mutex)]] attribute inside C struct #94216

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 12, 2024

Conversation

pdherbemont
Copy link
Contributor

Today, it's only supported inside C++ classes or top level C/C++
declaration.

I mostly copied and adapted over the code from the
[[counted_by(value)]] lookup.

Copy link

github-actions bot commented Jun 3, 2024

Thank you for submitting a Pull Request (PR) to the LLVM Project!

This PR will be automatically labeled and the relevant teams will be
notified.

If you wish to, you can add reviewers by using the "Reviewers" section on this page.

If this is not working for you, it is probably because you do not have write
permissions for the repository. In which case you can instead tag reviewers by
name in a comment by using @ followed by their GitHub username.

If you have received no comments on your PR for a week, you can request a review
by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate
is once a week. Please remember that you are asking for valuable time from other developers.

If you have further questions, they may be answered by the LLVM GitHub User Guide.

You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums.

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Jun 3, 2024
@llvmbot
Copy link
Member

llvmbot commented Jun 3, 2024

@llvm/pr-subscribers-clang

Author: Pierre d'Herbemont (pdherbemont)

Changes

Today, it's only supported inside C++ classes or top level C/C++
declaration.

I mostly copied and adapted over the code from the
[[counted_by(value)]] lookup.


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

4 Files Affected:

  • (modified) clang/include/clang/Parse/Parser.h (+6)
  • (modified) clang/lib/Parse/ParseDecl.cpp (+59)
  • (modified) clang/test/Sema/warn-thread-safety-analysis.c (+9-1)
  • (modified) clang/test/SemaCXX/warn-thread-safety-parsing.cpp (+1-5)
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index d054b8cf0d240..308f6ea121eab 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -3128,6 +3128,12 @@ class Parser : public CodeCompletionHandler {
                             IdentifierInfo *ScopeName, SourceLocation ScopeLoc,
                             ParsedAttr::Form Form);
 
+  void ParseGuardedByAttribute(IdentifierInfo &AttrName,
+                               SourceLocation AttrNameLoc,
+                               ParsedAttributes &Attrs,
+                               IdentifierInfo *ScopeName,
+                               SourceLocation ScopeLoc, ParsedAttr::Form Form);
+
   void ParseTypeofSpecifier(DeclSpec &DS);
   SourceLocation ParseDecltypeSpecifier(DeclSpec &DS);
   void AnnotateExistingDecltypeSpecifier(const DeclSpec &DS,
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index c528917437332..58a3de7210455 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -671,6 +671,14 @@ void Parser::ParseGNUAttributeArgs(
     ParseBoundsAttribute(*AttrName, AttrNameLoc, Attrs, ScopeName, ScopeLoc,
                          Form);
     return;
+  } else if (AttrKind == ParsedAttr::AT_GuardedBy) {
+    ParseGuardedByAttribute(*AttrName, AttrNameLoc, Attrs, ScopeName, ScopeLoc,
+                            Form);
+    return;
+  } else if (AttrKind == ParsedAttr::AT_PtGuardedBy) {
+    ParseGuardedByAttribute(*AttrName, AttrNameLoc, Attrs, ScopeName, ScopeLoc,
+                            Form);
+    return;
   } else if (AttrKind == ParsedAttr::AT_CXXAssume) {
     ParseCXXAssumeAttributeArg(Attrs, AttrName, AttrNameLoc, EndLoc, Form);
     return;
@@ -3330,6 +3338,57 @@ void Parser::DistributeCLateParsedAttrs(Decl *Dcl,
   }
 }
 
+/// GuardedBy attributes (e.g., guarded_by):
+///   AttrName '(' expression ')'
+void Parser::ParseGuardedByAttribute(IdentifierInfo &AttrName,
+                                     SourceLocation AttrNameLoc,
+                                     ParsedAttributes &Attrs,
+                                     IdentifierInfo *ScopeName,
+                                     SourceLocation ScopeLoc,
+                                     ParsedAttr::Form Form) {
+  assert(Tok.is(tok::l_paren) && "Attribute arg list not starting with '('");
+
+  BalancedDelimiterTracker Parens(*this, tok::l_paren);
+  Parens.consumeOpen();
+
+  if (Tok.is(tok::r_paren)) {
+    Diag(Tok.getLocation(), diag::err_argument_required_after_attribute);
+    Parens.consumeClose();
+    return;
+  }
+
+  ArgsVector ArgExprs;
+  // Don't evaluate argument when the attribute is ignored.
+  using ExpressionKind =
+      Sema::ExpressionEvaluationContextRecord::ExpressionKind;
+  EnterExpressionEvaluationContext EC(
+      Actions, Sema::ExpressionEvaluationContext::PotentiallyEvaluated, nullptr,
+      ExpressionKind::EK_BoundsAttrArgument);
+
+  ExprResult ArgExpr(
+      Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression()));
+
+  if (ArgExpr.isInvalid()) {
+    Parens.skipToEnd();
+    return;
+  }
+
+  ArgExprs.push_back(ArgExpr.get());
+
+  auto &AL = *Attrs.addNew(
+      &AttrName, SourceRange(AttrNameLoc, Parens.getCloseLocation()), ScopeName,
+      ScopeLoc, ArgExprs.data(), ArgExprs.size(), Form);
+
+  if (!Tok.is(tok::r_paren)) {
+    Diag(Tok.getLocation(), diag::err_attribute_wrong_number_arguments)
+        << AL << 1;
+    Parens.skipToEnd();
+    return;
+  }
+
+  Parens.consumeClose();
+}
+
 /// Bounds attributes (e.g., counted_by):
 ///   AttrName '(' expression ')'
 void Parser::ParseBoundsAttribute(IdentifierInfo &AttrName,
diff --git a/clang/test/Sema/warn-thread-safety-analysis.c b/clang/test/Sema/warn-thread-safety-analysis.c
index 642ea88ec3c96..abad65b0b9154 100644
--- a/clang/test/Sema/warn-thread-safety-analysis.c
+++ b/clang/test/Sema/warn-thread-safety-analysis.c
@@ -28,7 +28,12 @@
 struct LOCKABLE Mutex {};
 
 struct Foo {
-  struct Mutex *mu_;
+    struct Mutex *mu_;
+    struct Bar {
+        struct Mutex *other_mu;
+    } bar;
+  int  a_value GUARDED_BY(mu_);
+  int* a_ptr PT_GUARDED_BY(bar.other_mu);
 };
 
 // Declare mutex lock/unlock functions.
@@ -136,6 +141,9 @@ int main(void) {
     // Cleanup happens automatically -> no warning.
   }
 
+  foo_.a_value = 0; // expected-warning {{writing variable 'a_value' requires holding mutex 'mu_' exclusively}}
+  *foo_.a_ptr = 1; // expected-warning {{writing the value pointed to by 'a_ptr' requires holding mutex 'bar.other_mu' exclusively}}
+
   return 0;
 }
 
diff --git a/clang/test/SemaCXX/warn-thread-safety-parsing.cpp b/clang/test/SemaCXX/warn-thread-safety-parsing.cpp
index 0c5b0cc85897b..1626e8375892a 100644
--- a/clang/test/SemaCXX/warn-thread-safety-parsing.cpp
+++ b/clang/test/SemaCXX/warn-thread-safety-parsing.cpp
@@ -1516,11 +1516,7 @@ class Foo {
   mutable Mutex mu;
   int a GUARDED_BY(mu);
 
-  static int si GUARDED_BY(mu);
-//FIXME: Bug 32066 - Error should be emitted irrespective of C++ dialect
-#if __cplusplus <= 199711L
-  // expected-error@-3 {{invalid use of non-static data member 'mu'}}
-#endif
+  static int si GUARDED_BY(mu); // expected-error {{invalid use of non-static data member 'mu'}}
 
   static void foo() EXCLUSIVE_LOCKS_REQUIRED(mu);
 //FIXME: Bug 32066 - Error should be emitted irrespective of C++ dialect

@AaronBallman AaronBallman requested review from aaronpuchert and removed request for faisalv June 4, 2024 16:23
Copy link
Collaborator

@AaronBallman AaronBallman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for working on this!

The changes should come with a release note in clang/docs/ReleaseNotes.rst so users know about the new functionality.

One question I have is whether there will be follow-up work for other thread safety attributes (acquire_capability, try_acquire_capability, etc)?

Copy link
Contributor

@rapidsna rapidsna left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You may also want to consider making the attribute late parsed in C when -fexperimental-late-parse-attributes is enabled. See https://github.com/llvm/llvm-project/pull/93121/files#diff-ae2ec9524bdbeea1f06917607482634dd89af5bcbb929805032463e5dafe79e7R2260

That will allow the code like below:

 struct Foo {
   int a_value GUARDED_BY(mu_); // attribute comes before `mu_` which needs to be late parsed
   struct Mutext *mu_;
 }

@pdherbemont
Copy link
Contributor Author

One question I have is whether there will be follow-up work for other thread safety attributes (acquire_capability, try_acquire_capability, etc)?

They already work fine in C so far. But if you are aware of some issues don't hesitate to let me know!

Copy link
Contributor

@Endilll Endilll left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sema.h changes look good to me.

@AaronBallman
Copy link
Collaborator

One question I have is whether there will be follow-up work for other thread safety attributes (acquire_capability, try_acquire_capability, etc)?

They already work fine in C so far. But if you are aware of some issues don't hesitate to let me know!

Ah, sorry, of course I picked examples that appertain to functions rather than fields. But some of the other thread safety attributes have the same problem as guarded_by: https://godbolt.org/z/hWTMxzrns

@pdherbemont
Copy link
Contributor Author

You may also want to consider making the attribute late parsed in C when -fexperimental-late-parse-attributes is enabled. See https://github.com/llvm/llvm-project/pull/93121/files#diff-ae2ec9524bdbeea1f06917607482634dd89af5bcbb929805032463e5dafe79e7R2260

That will allow the code like below:

 struct Foo {
   int a_value GUARDED_BY(mu_); // attribute comes before `mu_` which needs to be late parsed
   struct Mutext *mu_;
 }

Adopted LateAttrParseExperimentalExt. Let me know if that looks okay.

@pdherbemont
Copy link
Contributor Author

One question I have is whether there will be follow-up work for other thread safety attributes (acquire_capability, try_acquire_capability, etc)?

They already work fine in C so far. But if you are aware of some issues don't hesitate to let me know!

Ah, sorry, of course I picked examples that appertain to functions rather than fields. But some of the other thread safety attributes have the same problem as guarded_by: https://godbolt.org/z/hWTMxzrns

Oh yes – maybe it makes sense to fix them in this commit. Let me try to look into that quickly.

@delcypher
Copy link
Contributor

@pdherbemont I've landed on your behalf. I tweaked the commit message to be more descriptive of the change we finally landed and noted that you are the author of this patch. Thanks again for working on this :)

@aeubanks
Copy link
Contributor

this seems to have broken code like this with

../../base/allocator/partition_allocator/src/partition_alloc/random.cc:33:69: error: invalid use of non-static data member 'lock_'
   33 |     internal::base::InsecureRandomGenerator instance_ PA_GUARDED_BY(lock_);
      |                                                                     ^~~~~
../../base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/thread_annotations.h:59:70: note: expanded from macro 'PA_GUARDED_BY'
   59 | #define PA_GUARDED_BY(x) PA_THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
      |                                                                      ^
../../base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/thread_annotations.h:44:60: note: expanded from macro 'PA_THREAD_ANNOTATION_ATTRIBUTE__'
   44 | #define PA_THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
      |                                                            ^
../../base/allocator/partition_allocator/src/partition_alloc/random.cc:35:65: error: invalid use of non-static data member 'lock_'
   35 |         internal::base::InsecureRandomGenerator)] PA_GUARDED_BY(lock_) = {};
      |                                                                 ^~~~~
../../base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/thread_annotations.h:59:70: note: expanded from macro 'PA_GUARDED_BY'
   59 | #define PA_GUARDED_BY(x) PA_THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
      |                                                                      ^
../../base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/thread_annotations.h:44:60: note: expanded from macro 'PA_THREAD_ANNOTATION_ATTRIBUTE__'
   44 | #define PA_THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
      |                                                            ^

is that intentional?

@aeubanks
Copy link
Contributor

reduced:

struct Lock {};
struct A {
        Lock lock;
        union {
                bool b __attribute__((guarded_by(lock)));
        };
};

seems like a regression, can we revert for now?

@delcypher
Copy link
Contributor

@aeubanks I'll revert. Is this example C or C++?

delcypher added a commit that referenced this pull request Jun 12, 2024
…C structs and support late parsing them (#94216)"

This reverts commit af0d712.

Reverting due to likely regression:

#94216 (comment)
@delcypher
Copy link
Contributor

Reverted in c9d5800

@delcypher
Copy link
Contributor

@pdherbemont Could you take a look at this?

@aeubanks
Copy link
Contributor

@aeubanks I'll revert. Is this example C or C++?

C++

@pdherbemont
Copy link
Contributor Author

Ah nice catch! Thank you @aeubanks and @delcypher – I am going to try to understand why (and add a new test)

@pdherbemont
Copy link
Contributor Author

So I found the cause of the regression: We used to parse the argument in an Unevaluated context in C++ mode. We now parse them in a PotentiallyEvaluated context. Switching to Unevaluated makes the C parsing code fail. A simple fix is to switch the context based on the language detected.

pdherbemont added a commit to pdherbemont/llvm-project that referenced this pull request Jun 13, 2024
…s and support late parsing them

This fixes llvm#20777. This replaces llvm#94216 which was reverted after being merged.

Previously the `guarded_by`, `pt_guarded_by`, `acquired_after`, and `acquired_before` attributes were only supported inside C++ classes or top level C/C++ declaration.

This patch allows these attributes to be added to struct members in C. These attributes have also now support experimental late parsing. This is off by default but can be enabled by passing `-fexperimental-late-parse-attributes`. This is useful for referring to a struct member after the annotated member. E.g.

```
struct Example {
  int a_value_defined_before __attribute__ ((guarded_by(a_mutex)));
  struct Mutex *a_mutex;
};
```
@pdherbemont
Copy link
Contributor Author

Follow-up PR #95455

pdherbemont added a commit to pdherbemont/llvm-project that referenced this pull request Jun 14, 2024
…s and support late parsing them

This fixes llvm#20777. This replaces llvm#94216 which was reverted after being merged.

Previously the `guarded_by`, `pt_guarded_by`, `acquired_after`, and `acquired_before` attributes were only supported inside C++ classes or top level C/C++ declaration.

This patch allows these attributes to be added to struct members in C. These attributes have also now support experimental late parsing. This is off by default but can be enabled by passing `-fexperimental-late-parse-attributes`. This is useful for referring to a struct member after the annotated member. E.g.

```
struct Example {
  int a_value_defined_before __attribute__ ((guarded_by(a_mutex)));
  struct Mutex *a_mutex;
};
```
pdherbemont added a commit to pdherbemont/llvm-project that referenced this pull request Jun 14, 2024
…s and support late parsing them

This fixes llvm#20777. This replaces llvm#94216 which was reverted after being merged.

Previously the `guarded_by`, `pt_guarded_by`, `acquired_after`, and `acquired_before` attributes were only supported inside C++ classes or top level C/C++ declaration.

This patch allows these attributes to be added to struct members in C. These attributes have also now support experimental late parsing. This is off by default but can be enabled by passing `-fexperimental-late-parse-attributes`. This is useful for referring to a struct member after the annotated member. E.g.

```
struct Example {
  int a_value_defined_before __attribute__ ((guarded_by(a_mutex)));
  struct Mutex *a_mutex;
};
```
pdherbemont added a commit to pdherbemont/llvm-project that referenced this pull request Jul 1, 2024
…s and support late parsing them

This fixes llvm#20777. This replaces llvm#94216 which was reverted after being merged.

Previously the `guarded_by`, `pt_guarded_by`, `acquired_after`, and `acquired_before` attributes were only supported inside C++ classes or top level C/C++ declaration.

This patch allows these attributes to be added to struct members in C. These attributes have also now support experimental late parsing. This is off by default but can be enabled by passing `-fexperimental-late-parse-attributes`. This is useful for referring to a struct member after the annotated member. E.g.

```
struct Example {
  int a_value_defined_before __attribute__ ((guarded_by(a_mutex)));
  struct Mutex *a_mutex;
};
```
pdherbemont added a commit to pdherbemont/llvm-project that referenced this pull request Jul 1, 2024
…s and support late parsing them

This fixes llvm#20777. This replaces llvm#94216 which was reverted after being merged.

Previously the `guarded_by`, `pt_guarded_by`, `acquired_after`, and `acquired_before` attributes were only supported inside C++ classes or top level C/C++ declaration.

This patch allows these attributes to be added to struct members in C. These attributes have also now support experimental late parsing. This is off by default but can be enabled by passing `-fexperimental-late-parse-attributes`. This is useful for referring to a struct member after the annotated member. E.g.

```
struct Example {
  int a_value_defined_before __attribute__ ((guarded_by(a_mutex)));
  struct Mutex *a_mutex;
};
```
pdherbemont added a commit to pdherbemont/llvm-project that referenced this pull request Jul 9, 2024
…s and support late parsing them

This fixes llvm#20777. This replaces llvm#94216 which was reverted after being merged.

Previously the `guarded_by`, `pt_guarded_by`, `acquired_after`, and `acquired_before` attributes were only supported inside C++ classes or top level C/C++ declaration.

This patch allows these attributes to be added to struct members in C. These attributes have also now support experimental late parsing. This is off by default but can be enabled by passing `-fexperimental-late-parse-attributes`. This is useful for referring to a struct member after the annotated member. E.g.

```
struct Example {
  int a_value_defined_before __attribute__ ((guarded_by(a_mutex)));
  struct Mutex *a_mutex;
};
```
delcypher pushed a commit that referenced this pull request Jul 9, 2024
…s and support late parsing them (#95455)

This fixes #20777. This replaces #94216 which was reverted after being
merged.

Previously the `guarded_by`, `pt_guarded_by`, `acquired_after`, and
`acquired_before` attributes were only supported inside C++ classes or
top level C/C++ declaration.

This patch allows these attributes to be added to struct members in C.
These attributes have also now support experimental late parsing. This
is off by default but can be enabled by passing
`-fexperimental-late-parse-attributes`. This is useful for referring to
a struct member after the annotated member. E.g.

```
struct Example {
  int a_value_defined_before __attribute__ ((guarded_by(a_mutex)));
  struct Mutex *a_mutex;
};
```
aaryanshukla pushed a commit to aaryanshukla/llvm-project that referenced this pull request Jul 14, 2024
…s and support late parsing them (llvm#95455)

This fixes llvm#20777. This replaces llvm#94216 which was reverted after being
merged.

Previously the `guarded_by`, `pt_guarded_by`, `acquired_after`, and
`acquired_before` attributes were only supported inside C++ classes or
top level C/C++ declaration.

This patch allows these attributes to be added to struct members in C.
These attributes have also now support experimental late parsing. This
is off by default but can be enabled by passing
`-fexperimental-late-parse-attributes`. This is useful for referring to
a struct member after the annotated member. E.g.

```
struct Example {
  int a_value_defined_before __attribute__ ((guarded_by(a_mutex)));
  struct Mutex *a_mutex;
};
```
@bvanassche
Copy link

Thread-safety attributes of cleanup functions are ignored. Does anyone know how to fix this? See also #122758.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants