131512811SUtkarsh Saxena // RUN: %check_clang_tidy -std=c++20 %s misc-coroutine-hostile-raii %t \ 2c9444439SUtkarsh Saxena // RUN: -config="{CheckOptions: {\ 3c9444439SUtkarsh Saxena // RUN: misc-coroutine-hostile-raii.RAIITypesList: 'my::Mutex; ::my::other::Mutex', \ 4*729657d6SUtkarsh Saxena // RUN: misc-coroutine-hostile-raii.AllowedAwaitablesList: 'safe::awaitable; ::transformable::awaitable' \ 5c9444439SUtkarsh Saxena // RUN: }}" 631512811SUtkarsh Saxena 731512811SUtkarsh Saxena namespace std { 831512811SUtkarsh Saxena 931512811SUtkarsh Saxena template <typename R, typename...> struct coroutine_traits { 1031512811SUtkarsh Saxena using promise_type = typename R::promise_type; 1131512811SUtkarsh Saxena }; 1231512811SUtkarsh Saxena 1331512811SUtkarsh Saxena template <typename Promise = void> struct coroutine_handle; 1431512811SUtkarsh Saxena 1531512811SUtkarsh Saxena template <> struct coroutine_handle<void> { from_addressstd::coroutine_handle1631512811SUtkarsh Saxena static coroutine_handle from_address(void *addr) noexcept { 1731512811SUtkarsh Saxena coroutine_handle me; 1831512811SUtkarsh Saxena me.ptr = addr; 1931512811SUtkarsh Saxena return me; 2031512811SUtkarsh Saxena } operator ()std::coroutine_handle2131512811SUtkarsh Saxena void operator()() { resume(); } addressstd::coroutine_handle2231512811SUtkarsh Saxena void *address() const noexcept { return ptr; } resumestd::coroutine_handle2331512811SUtkarsh Saxena void resume() const { } destroystd::coroutine_handle2431512811SUtkarsh Saxena void destroy() const { } donestd::coroutine_handle2531512811SUtkarsh Saxena bool done() const { return true; } operator =std::coroutine_handle2631512811SUtkarsh Saxena coroutine_handle &operator=(decltype(nullptr)) { 2731512811SUtkarsh Saxena ptr = nullptr; 2831512811SUtkarsh Saxena return *this; 2931512811SUtkarsh Saxena } coroutine_handlestd::coroutine_handle3031512811SUtkarsh Saxena coroutine_handle(decltype(nullptr)) : ptr(nullptr) {} coroutine_handlestd::coroutine_handle3131512811SUtkarsh Saxena coroutine_handle() : ptr(nullptr) {} 3231512811SUtkarsh Saxena // void reset() { ptr = nullptr; } // add to P0057? operator boolstd::coroutine_handle3331512811SUtkarsh Saxena explicit operator bool() const { return ptr; } 3431512811SUtkarsh Saxena 3531512811SUtkarsh Saxena protected: 3631512811SUtkarsh Saxena void *ptr; 3731512811SUtkarsh Saxena }; 3831512811SUtkarsh Saxena 3931512811SUtkarsh Saxena template <typename Promise> struct coroutine_handle : coroutine_handle<> { 4031512811SUtkarsh Saxena using coroutine_handle<>::operator=; 4131512811SUtkarsh Saxena from_addressstd::coroutine_handle4231512811SUtkarsh Saxena static coroutine_handle from_address(void *addr) noexcept { 4331512811SUtkarsh Saxena coroutine_handle me; 4431512811SUtkarsh Saxena me.ptr = addr; 4531512811SUtkarsh Saxena return me; 4631512811SUtkarsh Saxena } 4731512811SUtkarsh Saxena promisestd::coroutine_handle4831512811SUtkarsh Saxena Promise &promise() const { 4931512811SUtkarsh Saxena return *reinterpret_cast<Promise *>( 5031512811SUtkarsh Saxena __builtin_coro_promise(ptr, alignof(Promise), false)); 5131512811SUtkarsh Saxena } from_promisestd::coroutine_handle5231512811SUtkarsh Saxena static coroutine_handle from_promise(Promise &promise) { 5331512811SUtkarsh Saxena coroutine_handle p; 5431512811SUtkarsh Saxena p.ptr = __builtin_coro_promise(&promise, alignof(Promise), true); 5531512811SUtkarsh Saxena return p; 5631512811SUtkarsh Saxena } 5731512811SUtkarsh Saxena }; 5831512811SUtkarsh Saxena 5931512811SUtkarsh Saxena struct suspend_always { await_readystd::suspend_always6031512811SUtkarsh Saxena bool await_ready() noexcept { return false; } await_suspendstd::suspend_always6131512811SUtkarsh Saxena void await_suspend(std::coroutine_handle<>) noexcept {} await_resumestd::suspend_always6231512811SUtkarsh Saxena void await_resume() noexcept {} 6331512811SUtkarsh Saxena }; 6431512811SUtkarsh Saxena } // namespace std 6531512811SUtkarsh Saxena 6631512811SUtkarsh Saxena struct ReturnObject { 6731512811SUtkarsh Saxena struct promise_type { get_return_objectReturnObject::promise_type6831512811SUtkarsh Saxena ReturnObject get_return_object() { return {}; } initial_suspendReturnObject::promise_type6931512811SUtkarsh Saxena std::suspend_always initial_suspend() { return {}; } final_suspendReturnObject::promise_type7031512811SUtkarsh Saxena std::suspend_always final_suspend() noexcept { return {}; } unhandled_exceptionReturnObject::promise_type7131512811SUtkarsh Saxena void unhandled_exception() {} yield_valueReturnObject::promise_type7231512811SUtkarsh Saxena std::suspend_always yield_value(int value) { return {}; } 7331512811SUtkarsh Saxena }; 7431512811SUtkarsh Saxena }; 7531512811SUtkarsh Saxena 7631512811SUtkarsh Saxena #define SCOPED_LOCKABLE __attribute__ ((scoped_lockable)) 7731512811SUtkarsh Saxena 7831512811SUtkarsh Saxena namespace absl { 7931512811SUtkarsh Saxena class SCOPED_LOCKABLE Mutex {}; 8031512811SUtkarsh Saxena using Mutex2 = Mutex; 8131512811SUtkarsh Saxena } // namespace absl 8231512811SUtkarsh Saxena BasicWarning()8331512811SUtkarsh SaxenaReturnObject BasicWarning() { 8431512811SUtkarsh Saxena absl::Mutex mtx; 8531512811SUtkarsh Saxena // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: 'mtx' holds a lock across a suspension point of coroutine and could be unlocked by a different thread [misc-coroutine-hostile-raii] 8631512811SUtkarsh Saxena int no_warning; 8731512811SUtkarsh Saxena { 8831512811SUtkarsh Saxena co_yield 1; 8931512811SUtkarsh Saxena // CHECK-MESSAGES: :[[@LINE-1]]:5: note: suspension point is here 9031512811SUtkarsh Saxena } 9131512811SUtkarsh Saxena } 9231512811SUtkarsh Saxena BasicNoWarning()9331512811SUtkarsh SaxenaReturnObject BasicNoWarning() { 9431512811SUtkarsh Saxena co_yield 1; 9531512811SUtkarsh Saxena { absl::Mutex no_warning; } 9631512811SUtkarsh Saxena int no_warning; 9731512811SUtkarsh Saxena { 9831512811SUtkarsh Saxena co_yield 1; 9931512811SUtkarsh Saxena absl::Mutex no_warning; 10031512811SUtkarsh Saxena } 10131512811SUtkarsh Saxena co_yield 1; 10231512811SUtkarsh Saxena } 10331512811SUtkarsh Saxena scopedLockableTest()10431512811SUtkarsh SaxenaReturnObject scopedLockableTest() { 10531512811SUtkarsh Saxena co_yield 0; 10631512811SUtkarsh Saxena absl::Mutex a; 10731512811SUtkarsh Saxena // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: 'a' holds a lock across a suspension point of coroutine and could be unlocked by a different thread [misc-coroutine-hostile-raii] 10831512811SUtkarsh Saxena absl::Mutex2 b; 10931512811SUtkarsh Saxena // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: 'b' holds a lock across a suspension point of coroutine and could be unlocked by a different thread [misc-coroutine-hostile-raii] 11031512811SUtkarsh Saxena { 11131512811SUtkarsh Saxena absl::Mutex no_warning_1; 11231512811SUtkarsh Saxena { absl::Mutex no_warning_2; } 11331512811SUtkarsh Saxena } 11431512811SUtkarsh Saxena 11531512811SUtkarsh Saxena co_yield 1; 11631512811SUtkarsh Saxena // CHECK-MESSAGES: :[[@LINE-1]]:5: note: suspension point is here 11731512811SUtkarsh Saxena absl::Mutex c; 11831512811SUtkarsh Saxena // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: 'c' holds a lock across a suspension point of coroutine and could be unlocked by a different thread [misc-coroutine-hostile-raii] 11931512811SUtkarsh Saxena co_await std::suspend_always{}; 12031512811SUtkarsh Saxena // CHECK-MESSAGES: :[[@LINE-1]]:5: note: suspension point is here 12131512811SUtkarsh Saxena for(int i=1; i<=10; ++i ) { 12231512811SUtkarsh Saxena absl::Mutex d; 12331512811SUtkarsh Saxena // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: 'd' holds a lock across a suspension point of coroutine and could be unlocked by a different thread [misc-coroutine-hostile-raii] 12431512811SUtkarsh Saxena co_await std::suspend_always{}; 12531512811SUtkarsh Saxena // CHECK-MESSAGES: :[[@LINE-1]]:7: note: suspension point is here 12631512811SUtkarsh Saxena co_yield 1; 12731512811SUtkarsh Saxena absl::Mutex no_warning_3; 12831512811SUtkarsh Saxena } 12931512811SUtkarsh Saxena if (true) { 13031512811SUtkarsh Saxena absl::Mutex e; 13131512811SUtkarsh Saxena // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: 'e' holds a lock across a suspension point of coroutine and could be unlocked by a different thread [misc-coroutine-hostile-raii] 13231512811SUtkarsh Saxena co_yield 1; 13331512811SUtkarsh Saxena // CHECK-MESSAGES: :[[@LINE-1]]:7: note: suspension point is here 13431512811SUtkarsh Saxena absl::Mutex no_warning_4; 13531512811SUtkarsh Saxena } 13631512811SUtkarsh Saxena absl::Mutex no_warning_5; 13731512811SUtkarsh Saxena } 13831512811SUtkarsh Saxena 139*729657d6SUtkarsh Saxena // ================================================================================ 140*729657d6SUtkarsh Saxena // Safe awaitable 141*729657d6SUtkarsh Saxena // ================================================================================ 142c9444439SUtkarsh Saxena namespace safe { 143c9444439SUtkarsh Saxena struct awaitable { await_readysafe::awaitable144c9444439SUtkarsh Saxena bool await_ready() noexcept { return false; } await_suspendsafe::awaitable145c9444439SUtkarsh Saxena void await_suspend(std::coroutine_handle<>) noexcept {} await_resumesafe::awaitable146c9444439SUtkarsh Saxena void await_resume() noexcept {} 147c9444439SUtkarsh Saxena }; 148c9444439SUtkarsh Saxena } // namespace safe RAIISafeSuspendTest()149c9444439SUtkarsh SaxenaReturnObject RAIISafeSuspendTest() { 150c9444439SUtkarsh Saxena absl::Mutex a; 151c9444439SUtkarsh Saxena co_await safe::awaitable{}; 152c9444439SUtkarsh Saxena using other = safe::awaitable; 153c9444439SUtkarsh Saxena co_await other{}; 154c9444439SUtkarsh Saxena } 155c9444439SUtkarsh Saxena 156*729657d6SUtkarsh Saxena // ================================================================================ 157*729657d6SUtkarsh Saxena // Safe transformable awaitable 158*729657d6SUtkarsh Saxena // ================================================================================ 159*729657d6SUtkarsh Saxena struct transformable { struct awaitable{}; }; 160*729657d6SUtkarsh Saxena using alias_transformable_awaitable = transformable::awaitable; 161*729657d6SUtkarsh Saxena struct UseTransformAwaitable { 162*729657d6SUtkarsh Saxena struct promise_type { get_return_objectUseTransformAwaitable::promise_type163*729657d6SUtkarsh Saxena UseTransformAwaitable get_return_object() { return {}; } initial_suspendUseTransformAwaitable::promise_type164*729657d6SUtkarsh Saxena std::suspend_always initial_suspend() { return {}; } final_suspendUseTransformAwaitable::promise_type165*729657d6SUtkarsh Saxena std::suspend_always final_suspend() noexcept { return {}; } unhandled_exceptionUseTransformAwaitable::promise_type166*729657d6SUtkarsh Saxena void unhandled_exception() {} await_transformUseTransformAwaitable::promise_type167*729657d6SUtkarsh Saxena std::suspend_always await_transform(transformable::awaitable) { return {}; } 168*729657d6SUtkarsh Saxena }; 169*729657d6SUtkarsh Saxena }; 170*729657d6SUtkarsh Saxena retAwaitable()171*729657d6SUtkarsh Saxenaauto retAwaitable() { return transformable::awaitable{}; } RAIISafeSuspendTest2()172*729657d6SUtkarsh SaxenaUseTransformAwaitable RAIISafeSuspendTest2() { 173*729657d6SUtkarsh Saxena absl::Mutex a; 174*729657d6SUtkarsh Saxena co_await retAwaitable(); 175*729657d6SUtkarsh Saxena co_await transformable::awaitable{}; 176*729657d6SUtkarsh Saxena co_await alias_transformable_awaitable{}; 177*729657d6SUtkarsh Saxena } 178*729657d6SUtkarsh Saxena 179*729657d6SUtkarsh Saxena // ================================================================================ 180*729657d6SUtkarsh Saxena // Lambdas 181*729657d6SUtkarsh Saxena // ================================================================================ lambda()18231512811SUtkarsh Saxenavoid lambda() { 18331512811SUtkarsh Saxena absl::Mutex no_warning; 18431512811SUtkarsh Saxena auto lambda = []() -> ReturnObject { 18531512811SUtkarsh Saxena co_await std::suspend_always{}; 18631512811SUtkarsh Saxena absl::Mutex a; 18731512811SUtkarsh Saxena // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: 'a' holds a lock across a suspension point of coroutine and could be unlocked by a different thread [misc-coroutine-hostile-raii] 18831512811SUtkarsh Saxena co_yield 1; 18931512811SUtkarsh Saxena // CHECK-MESSAGES: :[[@LINE-1]]:5: note: suspension point is here 19031512811SUtkarsh Saxena co_await std::suspend_always{}; 19131512811SUtkarsh Saxena co_yield 1; 19231512811SUtkarsh Saxena }; 19331512811SUtkarsh Saxena absl::Mutex no_warning_2; 19431512811SUtkarsh Saxena } 19531512811SUtkarsh Saxena 196*729657d6SUtkarsh Saxena // ================================================================================ 197*729657d6SUtkarsh Saxena // Denylisted RAII 198*729657d6SUtkarsh Saxena // ================================================================================ 19931512811SUtkarsh Saxena template<class T> raii_in_template()20031512811SUtkarsh SaxenaReturnObject raii_in_template(){ 20131512811SUtkarsh Saxena T a; 20231512811SUtkarsh Saxena // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: 'a' holds a lock across a suspension point of coroutine and could be unlocked by a different thread [misc-coroutine-hostile-raii] 20331512811SUtkarsh Saxena co_yield 1; 20431512811SUtkarsh Saxena // CHECK-MESSAGES: :[[@LINE-1]]:3: note: suspension point is here 20531512811SUtkarsh Saxena } foo_template()20631512811SUtkarsh Saxenavoid foo_template() { raii_in_template<absl::Mutex>(); } 20731512811SUtkarsh Saxena 20831512811SUtkarsh Saxena namespace my { 20931512811SUtkarsh Saxena class Mutex{}; 21031512811SUtkarsh Saxena namespace other { 21131512811SUtkarsh Saxena class Mutex{}; 21231512811SUtkarsh Saxena } // namespace other 21331512811SUtkarsh Saxena 21431512811SUtkarsh Saxena using Mutex2 = Mutex; 21531512811SUtkarsh Saxena } // namespace my 21631512811SUtkarsh Saxena denyListTest()21731512811SUtkarsh SaxenaReturnObject denyListTest() { 21831512811SUtkarsh Saxena my::Mutex a; 21931512811SUtkarsh Saxena // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: 'a' persists across a suspension point of coroutine [misc-coroutine-hostile-raii] 22031512811SUtkarsh Saxena my::other::Mutex b; 22131512811SUtkarsh Saxena // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: 'b' persists across a suspension point of coroutine [misc-coroutine-hostile-raii] 22231512811SUtkarsh Saxena my::Mutex2 c; 22331512811SUtkarsh Saxena // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 'c' persists across a suspension point of coroutine [misc-coroutine-hostile-raii] 22431512811SUtkarsh Saxena co_yield 1; 22531512811SUtkarsh Saxena // CHECK-MESSAGES: :[[@LINE-1]]:5: note: suspension point is here 22631512811SUtkarsh Saxena } 22731512811SUtkarsh Saxena referenceTest(my::Mutex & ref)22831512811SUtkarsh SaxenaReturnObject referenceTest(my::Mutex& ref) { 22931512811SUtkarsh Saxena my::Mutex& a = ref; 23031512811SUtkarsh Saxena co_yield 1; 23131512811SUtkarsh Saxena } pointerTest(my::Mutex * ref)23231512811SUtkarsh SaxenaReturnObject pointerTest(my::Mutex* ref) { 23331512811SUtkarsh Saxena my::Mutex* a = ref; 23431512811SUtkarsh Saxena co_yield 1; 23531512811SUtkarsh Saxena } 23631512811SUtkarsh Saxena functionArgTest(my::Mutex ref)23731512811SUtkarsh SaxenaReturnObject functionArgTest(my::Mutex ref) { 23831512811SUtkarsh Saxena co_yield 1; 23931512811SUtkarsh Saxena } 240