[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)
Doug Wyatt via cfe-commits
cfe-commits at lists.llvm.org
Tue Aug 6 14:40:24 PDT 2024
================
@@ -0,0 +1,194 @@
+// RUN: %clang_cc1 -fsyntax-only -fblocks -fcxx-exceptions -verify %s
+// These are in a separate file because errors (e.g. incompatible attributes) currently prevent
+// the AnalysisBasedWarnings 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 nl1() [[clang::nonblocking]]
+{
+ auto* pInt = new int; // expected-warning {{'nonblocking' function must not allocate or deallocate memory}}
+}
+
+void nl2() [[clang::nonblocking]]
+{
+ static int global; // expected-warning {{'nonblocking' function must not have static locals}}
+}
+
+void nl3() [[clang::nonblocking]]
+{
+ try {
+ throw 42; // expected-warning {{'nonblocking' function must not throw or catch exceptions}}
+ }
+ catch (...) { // expected-warning {{'nonblocking' function must not throw or catch exceptions}}
+ }
+}
+
+void nl4_inline() {}
+void nl4_not_inline(); // expected-note {{function cannot be inferred 'nonblocking' because it has no definition in this translation unit}}
+
+void nl4() [[clang::nonblocking]]
+{
+ nl4_inline(); // OK
+ nl4_not_inline(); // expected-warning {{'nonblocking' function must not call non-'nonblocking' function}}
+}
+
+
+struct HasVirtual {
+ virtual void unsafe(); // expected-note {{virtual method cannot be inferred 'nonblocking'}}
+};
+
+void nl5() [[clang::nonblocking]]
+{
+ HasVirtual hv;
+ hv.unsafe(); // expected-warning {{'nonblocking' function must not call non-'nonblocking' function}}
+}
+
+void nl6_unsafe(); // expected-note {{function cannot be inferred 'nonblocking' because it has no definition in this translation unit}}
+void nl6_transitively_unsafe()
+{
+ nl6_unsafe(); // expected-note {{function cannot be inferred 'nonblocking' because it calls non-'nonblocking' function}}
+}
+
+void nl6() [[clang::nonblocking]]
+{
+ nl6_transitively_unsafe(); // expected-warning {{'nonblocking' function must not call non-'nonblocking' function}}
+}
+
+thread_local int tl_var{ 42 };
+
+bool tl_test() [[clang::nonblocking]]
+{
+ return tl_var > 0; // expected-warning {{'nonblocking' function must not use thread-local variables}}
+}
+
+void nl7()
+{
+ // Make sure we verify blocks
+ auto blk = ^() [[clang::nonblocking]] {
+ throw 42; // expected-warning {{'nonblocking' function must not throw or catch exceptions}}
+ };
+}
+
+void nl8()
+{
+ // Make sure we verify lambdas
+ auto lambda = []() [[clang::nonblocking]] {
+ throw 42; // expected-warning {{'nonblocking' function must not throw or catch exceptions}}
+ };
+}
+
+// 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 {{'nonblocking' function 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/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 nl9() [[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 {{'nonblocking' function must not call non-'nonblocking' function}} \
+ expected-note {{in template expansion here}}
+}
+
+void nl10(
+ void (*fp1)(), // expected-note {{function pointer cannot be inferred 'nonblocking'}}
+ void (*fp2)() [[clang::nonblocking]]
+ ) [[clang::nonblocking]]
+{
+ fp1(); // expected-warning {{'nonblocking' function must not call non-'nonblocking' function}}
+ fp2();
+}
+
+// Interactions with nonblocking(false)
+void nl11_no_inference_1() [[clang::nonblocking(false)]] // expected-note {{function does not permit inference of 'nonblocking'}}
+{
+}
+void nl11_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'}}
+};
+
+void nl11() [[clang::nonblocking]]
+{
+ nl11_no_inference_1(); // expected-warning {{'nonblocking' function must not call non-'nonblocking' function}}
+ nl11_no_inference_2(); // expected-warning {{'nonblocking' function must not call non-'nonblocking' function}}
+
+ ComputedNB<true> CNB_true;
+ CNB_true.method();
+
+ ComputedNB<false> CNB_false;
+ CNB_false.method(); // expected-warning {{'nonblocking' function must not call non-'nonblocking' function}}
+}
+
+// Verify that when attached to a redeclaration, the attribute successfully attaches.
+void nl12() {
+ static int x; // expected-warning {{'nonblocking' function must not have static locals}}
+}
+void nl12() [[clang::nonblocking]];
+void nl13() [[clang::nonblocking]] { nl12(); }
+
+// 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 nl17(void (^blk)() [[clang::nonblocking]]) [[clang::nonblocking]] {
+ blk();
+}
+
+// References to blocks
+void nl18(void (^block)() [[clang::nonblocking]]) [[clang::nonblocking]]
+{
+ auto &ref = block;
+ ref();
+}
+
+
+// --- nonblocking implies noexcept ---
+#pragma clang diagnostic warning "-Wperf-constraint-implies-noexcept"
+
+void nl19() [[clang::nonblocking]] // expected-warning {{'nonblocking' function should be declared noexcept}}
+{
+}
----------------
dougsonos wrote:
> More horrible test cases:
>
> ```c++
> bool bad();
>
> template <bool>
> requires requires { bad(); }
> void g() [[clang::nonblocking]] {}
> ```
I'm probably just thick today (and weak on C++20 concepts) but I would have thought that this would fail simply because `bad()` isn't `constexpr`.
https://github.com/llvm/llvm-project/pull/99656
More information about the cfe-commits
mailing list