// RUN: %clang_cc1 -fsyntax-only -fblocks -fcxx-exceptions -std=c++20 -verify -Wfunction-effects -Wno-vla-extension %s // These are in a separate file because errors (e.g. incompatible attributes) currently prevent // the FXAnalysis pass from running at all. // This diagnostic is re-enabled and exercised in isolation later in this file. #pragma clang diagnostic ignored "-Wperf-constraint-implies-noexcept" // --- CONSTRAINTS --- void nb1() [[clang::nonblocking]] { int *pInt = new int; // expected-warning {{function with 'nonblocking' attribute must not allocate or deallocate memory}} delete pInt; // expected-warning {{function with 'nonblocking' attribute must not allocate or deallocate memory}} } void nb2() [[clang::nonblocking]] { static int global; // expected-warning {{function with 'nonblocking' attribute must not have static local variables}} } void nb3() [[clang::nonblocking]] { try { throw 42; // expected-warning {{function with 'nonblocking' attribute must not throw or catch exceptions}} } catch (...) { // expected-warning {{function with 'nonblocking' attribute must not throw or catch exceptions}} } } void nb4_inline() {} void nb4_not_inline(); // expected-note {{declaration cannot be inferred 'nonblocking' because it has no definition in this translation unit}} void nb4() [[clang::nonblocking]] { nb4_inline(); // OK nb4_not_inline(); // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' function}} } struct HasVirtual { virtual void unsafe(); // expected-note {{virtual method cannot be inferred 'nonblocking'}} }; void nb5() [[clang::nonblocking]] { HasVirtual hv; hv.unsafe(); // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' function}} } void nb6_unsafe(); // expected-note {{declaration cannot be inferred 'nonblocking' because it has no definition in this translation unit}} void nb6_transitively_unsafe() { nb6_unsafe(); // expected-note {{function cannot be inferred 'nonblocking' because it calls non-'nonblocking' function}} } void nb6() [[clang::nonblocking]] { nb6_transitively_unsafe(); // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' function}} } thread_local int tl_var{ 42 }; bool tl_test() [[clang::nonblocking]] { return tl_var > 0; // expected-warning {{function with 'nonblocking' attribute must not use thread-local variables}} } void nb7() { // Make sure we verify blocks auto blk = ^() [[clang::nonblocking]] { throw 42; // expected-warning {{block with 'nonblocking' attribute must not throw or catch exceptions}} }; } void nb8() { // Make sure we verify lambdas auto lambda = []() [[clang::nonblocking]] { throw 42; // expected-warning {{lambda with 'nonblocking' attribute must not throw or catch exceptions}} }; } void nb8a() [[clang::nonblocking]] { // A blocking lambda shouldn't make the outer function unsafe. auto unsafeLambda = []() { throw 42; }; } void nb8b() [[clang::nonblocking]] { // An unsafe lambda capture makes the outer function unsafe. auto unsafeCapture = [foo = new int]() { // expected-warning {{function with 'nonblocking' attribute must not allocate or deallocate memory}} delete foo; }; } void nb8c() { // An unsafe lambda capture does not make the lambda unsafe. auto unsafeCapture = [foo = new int]() [[clang::nonblocking]] { }; } // Make sure template expansions are found and verified. template struct Adder { static T add_explicit(T x, T y) [[clang::nonblocking]] { return x + y; // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' function}} } static T add_implicit(T x, T y) { return x + y; // expected-note {{function cannot be inferred 'nonblocking' because it calls non-'nonblocking' function}} } }; struct Stringy { friend Stringy operator+(const Stringy& x, const Stringy& y) { // Do something inferably unsafe auto* z = new char[42]; // expected-note {{function cannot be inferred 'nonblocking' because it allocates or deallocates memory}} return {}; } }; struct Stringy2 { friend Stringy2 operator+(const Stringy2& x, const Stringy2& y) { // Do something inferably unsafe throw 42; // expected-note {{function cannot be inferred 'nonblocking' because it throws or catches exceptions}} } }; void nb9() [[clang::nonblocking]] { Adder::add_explicit(1, 2); Adder::add_implicit(1, 2); Adder::add_explicit({}, {}); // expected-note {{in template expansion here}} Adder::add_implicit({}, {}); // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' function}} \ expected-note {{in template expansion here}} } // Make sure we verify lambdas produced from template expansions. struct HasTemplatedLambda { void (*fptr)() [[clang::nonblocking]]; template HasTemplatedLambda(const C&) : fptr{ []() [[clang::nonblocking]] { auto* y = new int; // expected-warning {{lambda with 'nonblocking' attribute must not allocate or deallocate memory}} } } {} }; void nb9a() { HasTemplatedLambda bad(42); } // Templated function and lambda. template void TemplatedFunc(T x) [[clang::nonblocking]] { auto* ptr = new T; // expected-warning {{function with 'nonblocking' attribute must not allocate or deallocate memory}} } void nb9b() [[clang::nonblocking]] { TemplatedFunc(42); // expected-note {{in template expansion here}} auto foo = [](auto x) [[clang::nonblocking]] { auto* ptr = new int; // expected-warning {{lambda with 'nonblocking' attribute must not allocate or deallocate memory}} return x; }; // Note that foo() won't be validated unless instantiated. foo(42); } void nb10( void (*fp1)(), // expected-note {{function pointer cannot be inferred 'nonblocking'}} void (*fp2)() [[clang::nonblocking]] ) [[clang::nonblocking]] { fp1(); // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' function}} fp2(); // When there's a cast, there's a separate diagnostic. static_cast(fp1)(); // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' expression}} } // Expression involving indirection int nb10a() [[clang::nonblocking]]; int nb10b() [[clang::nonblocking]]; int blocking(); int nb10c(bool x) [[clang::nonblocking]] { int y = (x ? nb10a : blocking)(); // expected-warning {{attribute 'nonblocking' should not be added via type conversion}} return (x ? nb10a : nb10b)(); // No diagnostic. } // Interactions with nonblocking(false) void nb11_no_inference_1() [[clang::nonblocking(false)]] // expected-note {{function does not permit inference of 'nonblocking'}} { } void nb11_no_inference_2() [[clang::nonblocking(false)]]; // expected-note {{function does not permit inference of 'nonblocking'}} template struct ComputedNB { void method() [[clang::nonblocking(V)]]; // expected-note {{function does not permit inference of 'nonblocking' because it is declared 'blocking'}} }; void nb11() [[clang::nonblocking]] { nb11_no_inference_1(); // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' function}} nb11_no_inference_2(); // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' function}} ComputedNB CNB_true; CNB_true.method(); ComputedNB CNB_false; CNB_false.method(); // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' function}} } // Verify that when attached to a redeclaration, the attribute successfully attaches. void nb12() { static int x; // expected-warning {{function with 'nonblocking' attribute must not have static local variables}} } void nb12() [[clang::nonblocking]]; void nb13() [[clang::nonblocking]] { nb12(); } // C++ member function pointers struct PTMFTester { typedef void (PTMFTester::*ConvertFunction)() [[clang::nonblocking]]; void convert() [[clang::nonblocking]]; ConvertFunction mConvertFunc; }; void PTMFTester::convert() [[clang::nonblocking]] { (this->*mConvertFunc)(); } // Allow implicit conversion from array to pointer. void nb14(unsigned idx) [[clang::nonblocking]] { using FP = void (*)() [[clang::nonblocking]]; using FPArray = FP[2]; auto nb = +[]() [[clang::nonblocking]] {}; FPArray src{ nb, nullptr }; FP f = src[idx]; // This should not generate a warning. FP twoDim[2][2] = {}; FP g = twoDim[1][1]; FP vla[idx]; FP h = vla[0]; } // Block variables void nb17(void (^blk)() [[clang::nonblocking]]) [[clang::nonblocking]] { blk(); } // References to blocks void nb18(void (^block)() [[clang::nonblocking]]) [[clang::nonblocking]] { auto &ref = block; ref(); } // Builtin functions void nb19() [[clang::nonblocking]] { __builtin_assume(1); void *ptr = __builtin_malloc(1); // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' function '__builtin_malloc'}} __builtin_free(ptr); // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' function '__builtin_free'}} void *p2 = __builtin_operator_new(1); // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' function '__builtin_operator_new'}} __builtin_operator_delete(p2); // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' function '__builtin_operator_delete'}} } // Function try-block void catches() try {} catch (...) {} // expected-note {{function cannot be inferred 'nonblocking' because it throws or catches exceptions}} void nb20() [[clang::nonblocking]] { catches(); // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' function 'catches'}} } struct S { int x; S(int x) try : x(x) {} catch (...) {} // expected-note {{constructor cannot be inferred 'nonblocking' because it throws or catches exceptions}} S(double) : x((throw 3, 3)) {} // expected-note {{member initializer cannot be inferred 'nonblocking' because it throws or catches exceptions}} \ expected-note {{in constructor here}} }; int badi(); // expected-note {{declaration cannot be inferred 'nonblocking' because it has no definition in this translation unit}} \ // expected-note {{declaration cannot be inferred 'nonblocking' because it has no definition in this translation unit}} struct A { // expected-note {{in implicit constructor here}} int x = (throw 3, 3); // expected-note {{member initializer cannot be inferred 'nonblocking' because it throws or catches exceptions}} }; struct B { int y = badi(); // expected-note {{member initializer cannot be inferred 'nonblocking' because it calls non-'nonblocking' function 'badi'}} }; void f() [[clang::nonblocking]] { S s1(3); // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' constructor 'S::S'}} S s2(3.0); // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' constructor 'S::S'}} A a; // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' constructor 'A::A'}} B b; // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' constructor 'B::B'}} } struct T { int x = badi(); // expected-warning {{member initializer of constructor with 'nonblocking' attribute must not call non-'nonblocking' function 'badi'}} T() [[clang::nonblocking]] {} // expected-note {{in constructor here}} T(int x) [[clang::nonblocking]] : x(x) {} // OK }; // Default arguments int badForDefaultArg(); // expected-note {{declaration cannot be inferred 'nonblocking' because it has no definition in this translation unit}} \ expected-note {{declaration cannot be inferred 'nonblocking' because it has no definition in this translation unit}} \ expected-note {{declaration cannot be inferred 'nonblocking' because it has no definition in this translation unit}} void hasDefaultArg(int param = badForDefaultArg()) { // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' function 'badForDefaultArg'}} \ expected-note {{function cannot be inferred 'nonblocking' because it calls non-'nonblocking' function 'badForDefaultArg'}} } void nb21() [[clang::nonblocking]] { hasDefaultArg(); // expected-note {{in evaluating default argument here}} \ expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' function 'hasDefaultArg'}} } void nb22(int param = badForDefaultArg()) [[clang::nonblocking]] { // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' function 'badForDefaultArg'}} } // Verify traversal of implicit code paths - constructors and destructors. struct Unsafe { static void problem1(); // expected-note {{declaration cannot be inferred 'nonblocking' because it has no definition in this translation unit}} static void problem2(); // expected-note {{declaration cannot be inferred 'nonblocking' because it has no definition in this translation unit}} Unsafe() { problem1(); } // expected-note {{constructor cannot be inferred 'nonblocking' because it calls non-'nonblocking' function 'Unsafe::problem1'}} ~Unsafe() { problem2(); } // expected-note {{destructor cannot be inferred 'nonblocking' because it calls non-'nonblocking' function 'Unsafe::problem2'}} Unsafe(int x); // expected-note {{declaration cannot be inferred 'nonblocking' because it has no definition in this translation unit}} expected-note {{declaration cannot be inferred 'nonblocking' because it has no definition in this translation unit}} // Delegating initializer. Unsafe(float y) [[clang::nonblocking]] : Unsafe(int(y)) {} // expected-warning {{constructor with 'nonblocking' attribute must not call non-'nonblocking' constructor 'Unsafe::Unsafe'}} }; struct DerivedFromUnsafe : public Unsafe { DerivedFromUnsafe() [[clang::nonblocking]] {} // expected-warning {{constructor with 'nonblocking' attribute must not call non-'nonblocking' constructor 'Unsafe::Unsafe'}} DerivedFromUnsafe(int x) [[clang::nonblocking]] : Unsafe(x) {} // expected-warning {{constructor with 'nonblocking' attribute must not call non-'nonblocking' constructor 'Unsafe::Unsafe'}} ~DerivedFromUnsafe() [[clang::nonblocking]] {} // expected-warning {{destructor with 'nonblocking' attribute must not call non-'nonblocking' destructor 'Unsafe::~Unsafe'}} }; // Don't try to follow a deleted destructor, as with std::optional. struct HasDtor { ~HasDtor() {} }; template struct Optional { union { char __null_state_; T __val_; }; bool engaged = false; ~Optional() { if (engaged) __val_.~T(); } }; void nb_opt() [[clang::nonblocking]] { Optional x; } // Virtual inheritance struct VBase { int *Ptr; VBase() { Ptr = new int; } // expected-note {{constructor cannot be inferred 'nonblocking' because it allocates or deallocates memory}} virtual ~VBase() { delete Ptr; } // expected-note {{virtual method cannot be inferred 'nonblocking'}} }; struct VDerived : virtual VBase { VDerived() [[clang::nonblocking]] {} // expected-warning {{constructor with 'nonblocking' attribute must not call non-'nonblocking' constructor 'VBase::VBase'}} ~VDerived() [[clang::nonblocking]] {} // expected-warning {{destructor with 'nonblocking' attribute must not call non-'nonblocking' destructor 'VBase::~VBase'}} }; // Contexts where there is no function call, no diagnostic. bool bad(); template requires requires { bad(); } void g() [[clang::nonblocking]] {} void g() [[clang::nonblocking]] { decltype(bad()) a; // doesn't generate a call so, OK [[maybe_unused]] auto b = noexcept(bad()); [[maybe_unused]] auto c = sizeof(bad()); #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wassume" [[assume(bad())]]; // never evaluated, but maybe still semantically questionable? #pragma clang diagnostic pop } // Make sure we are skipping concept requirements -- they can trigger an unexpected // warning involving use of a function pointer (e.g. std::reverse_iterator::operator== struct HasFoo { int foo() const { return 0; } }; template inline bool compare(const A& a, const B& b) requires requires { a.foo(); } { return a.foo() == b.foo(); } void nb25() [[clang::nonblocking]] { HasFoo a, b; compare(a, b); } // If the callee is both noreturn and noexcept, it presumably terminates. // Ignore it for the purposes of effect analysis. [[noreturn]] void abort_wrapper() noexcept; void nb26() [[clang::nonblocking]] { abort_wrapper(); // no diagnostic } // --- Make sure we don't traverse requires and noexcept clauses. --- // Apparently some requires clauses are able to be collapsed into a constant before the nonblocking // analysis sees any function calls. This example (extracted from a real-world case where // `operator&&` in , preceding the inclusion of ) is sufficiently complex // to look like it contains function calls. There may be simpler examples. namespace ExpectedTest { template inline constexpr bool is_copy_constructible_v = __is_constructible(_Tp, _Tp&); template struct enable_if {}; template struct enable_if { typedef _Tp type; }; template using enable_if_t = typename enable_if<_Bp, _Tp>::type; // Doesn't seem to matter whether the enable_if is true or false. template > = 0> inline bool operator&&(const E1& x, const E2& y); template class expected { public: constexpr expected() {} // This is a deliberate corruption of the real implementation for simplicity. constexpr expected(const expected&) requires(is_copy_constructible_v<_Tp> && is_copy_constructible_v<_Err>) = default; }; void test() [[clang::nonblocking]] { expected a; auto b = a; // Copy constructor. } } // namespace ExpectedTest // Make sure a function call in a noexcept() clause is ignored. constexpr bool foo() [[clang::nonblocking(false)]] { return true; } void nb27() noexcept(foo()) [[clang::nonblocking]] {} // Make sure that simple type traits don't cause violations. void nb28() [[clang::nonblocking]] { bool x = __is_constructible(int, const int&); } // --- nonblocking implies noexcept --- #pragma clang diagnostic warning "-Wperf-constraint-implies-noexcept" void needs_noexcept() [[clang::nonblocking]] // expected-warning {{function with 'nonblocking' attribute should be declared noexcept}} { auto lambda = []() [[clang::nonblocking]] {}; // expected-warning {{lambda with 'nonblocking' attribute should be declared noexcept}} }