diff --git a/clang/test/Sema/warn-thread-safety-analysis.c b/clang/test/Sema/warn-thread-safety-analysis.c index 549cb1231baa6..9ca4b580e2b13 100644 --- a/clang/test/Sema/warn-thread-safety-analysis.c +++ b/clang/test/Sema/warn-thread-safety-analysis.c @@ -94,6 +94,7 @@ int get_value(int *p) SHARED_LOCKS_REQUIRED(foo_.mu_){ } void unlock_scope(struct Mutex *const *mu) __attribute__((release_capability(**mu))); +void unlock_scope_type_erased(int **priv) __attribute__((release_capability(*(struct Mutex **)priv))); // Verify late parsing: #ifdef LATE_PARSING @@ -191,6 +192,13 @@ int main(void) { struct Mutex* const __attribute__((unused, cleanup(unlock_scope))) scope = &mu1; mutex_exclusive_lock(&mu1); // With basic alias analysis lock through mu1 also works. } + // Cleanup through cast alias pointer in a for-loop; a variant of this pattern + // appears in the Linux kernel for generic scoped guard macros. + for (int i = (mutex_exclusive_lock(foo_.mu_), 0), + *priv __attribute__((cleanup(unlock_scope_type_erased))) = (int *)(__UINTPTR_TYPE__)(foo_.mu_); + !i; i++) { + a_ = 42; + } 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}} diff --git a/clang/test/SemaCXX/warn-thread-safety-analysis.cpp b/clang/test/SemaCXX/warn-thread-safety-analysis.cpp index 7cb416d71569c..d5ae2eedc2bd7 100644 --- a/clang/test/SemaCXX/warn-thread-safety-analysis.cpp +++ b/clang/test/SemaCXX/warn-thread-safety-analysis.cpp @@ -7325,6 +7325,15 @@ void testBasicPointerAlias(Foo *f) { ptr->mu.Unlock(); // unlock through alias } +void testCastPointerAlias(Foo *f) { + // Cast to void* from unsigned long to test non-pointer cast indirection. + void *priv = (void *)(__UINTPTR_TYPE__)(&f->mu); + f->mu.Lock(); + f->data = 42; + auto *mu = (Mutex *)priv; + mu->Unlock(); +} + void testBasicPointerAliasNoInit(Foo *f) { Foo *ptr;