// RUN: %clang_cc1 -fsyntax-only -fblocks -fcxx-exceptions -std=c++20 -verify -Wfunction-effects %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 <typename T>
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<int>::add_explicit(1, 2);
Adder<int>::add_implicit(1, 2);
Adder<Stringy>::add_explicit({}, {}); // expected-note {{in template expansion here}}
Adder<Stringy2>::add_implicit({}, {}); // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' function}} \
expected-note {{in template expansion here}}
}
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<void (*)()>(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 <bool V>
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<true> CNB_true;
CNB_true.method();
ComputedNB<false> 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)();
}
// 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<T>.
struct HasDtor {
~HasDtor() {}
};
template <typename T>
struct Optional {
union {
char __null_state_;
T __val_;
};
bool engaged = false;
~Optional() {
if (engaged)
__val_.~T();
}
};
void nb_opt() [[clang::nonblocking]] {
Optional<HasDtor> 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 <bool>
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 <class A, class B>
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
}
// --- 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}}
}