xref: /llvm-project/clang/test/Sema/attr-nonblocking-constraints.cpp (revision 0417cd1b3e66c06966a3685f143df9228e2444b1)
1 // RUN: %clang_cc1 -fsyntax-only -fblocks -fcxx-exceptions -std=c++20 -verify -Wfunction-effects -Wno-vla-extension %s
2 // These are in a separate file because errors (e.g. incompatible attributes) currently prevent
3 // the FXAnalysis pass from running at all.
4 
5 // This diagnostic is re-enabled and exercised in isolation later in this file.
6 #pragma clang diagnostic ignored "-Wperf-constraint-implies-noexcept"
7 
8 // --- CONSTRAINTS ---
9 
10 void nb1() [[clang::nonblocking]]
11 {
12 	int *pInt = new int; // expected-warning {{function with 'nonblocking' attribute must not allocate or deallocate memory}}
13 	delete pInt; // expected-warning {{function with 'nonblocking' attribute must not allocate or deallocate memory}}
14 }
15 
16 void nb2() [[clang::nonblocking]]
17 {
18 	static int global; // expected-warning {{function with 'nonblocking' attribute must not have static local variables}}
19 }
20 
21 void nb3() [[clang::nonblocking]]
22 {
23 	try {
24 		throw 42; // expected-warning {{function with 'nonblocking' attribute must not throw or catch exceptions}}
25 	}
26 	catch (...) { // expected-warning {{function with 'nonblocking' attribute must not throw or catch exceptions}}
27 	}
28 }
29 
30 void nb4_inline() {}
31 void nb4_not_inline(); // expected-note {{declaration cannot be inferred 'nonblocking' because it has no definition in this translation unit}}
32 
33 void nb4() [[clang::nonblocking]]
34 {
35 	nb4_inline(); // OK
36 	nb4_not_inline(); // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' function}}
37 }
38 
39 
40 struct HasVirtual {
41 	virtual void unsafe(); // expected-note {{virtual method cannot be inferred 'nonblocking'}}
42 };
43 
44 void nb5() [[clang::nonblocking]]
45 {
46  	HasVirtual hv;
47  	hv.unsafe(); // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' function}}
48 }
49 
50 void nb6_unsafe(); // expected-note {{declaration cannot be inferred 'nonblocking' because it has no definition in this translation unit}}
51 void nb6_transitively_unsafe()
52 {
53 	nb6_unsafe(); // expected-note {{function cannot be inferred 'nonblocking' because it calls non-'nonblocking' function}}
54 }
55 
56 void nb6() [[clang::nonblocking]]
57 {
58 	nb6_transitively_unsafe(); // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' function}}
59 }
60 
61 thread_local int tl_var{ 42 };
62 
63 bool tl_test() [[clang::nonblocking]]
64 {
65 	return tl_var > 0; // expected-warning {{function with 'nonblocking' attribute must not use thread-local variables}}
66 }
67 
68 void nb7()
69 {
70 	// Make sure we verify blocks
71 	auto blk = ^() [[clang::nonblocking]] {
72 		throw 42; // expected-warning {{block with 'nonblocking' attribute must not throw or catch exceptions}}
73 	};
74 }
75 
76 void nb8()
77 {
78 	// Make sure we verify lambdas
79 	auto lambda = []() [[clang::nonblocking]] {
80 		throw 42; // expected-warning {{lambda with 'nonblocking' attribute must not throw or catch exceptions}}
81 	};
82 }
83 
84 void nb8a() [[clang::nonblocking]]
85 {
86 	// A blocking lambda shouldn't make the outer function unsafe.
87 	auto unsafeLambda = []() {
88 		throw 42;
89 	};
90 }
91 
92 void nb8b() [[clang::nonblocking]]
93 {
94 	// An unsafe lambda capture makes the outer function unsafe.
95 	auto unsafeCapture = [foo = new int]() { // expected-warning {{function with 'nonblocking' attribute must not allocate or deallocate memory}}
96 		delete foo;
97 	};
98 }
99 
100 void nb8c()
101 {
102 	// An unsafe lambda capture does not make the lambda unsafe.
103 	auto unsafeCapture = [foo = new int]() [[clang::nonblocking]] {
104 	};
105 }
106 
107 // Make sure template expansions are found and verified.
108 	template <typename T>
109 	struct Adder {
110 		static T add_explicit(T x, T y) [[clang::nonblocking]]
111 		{
112 			return x + y; // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' function}}
113 		}
114 		static T add_implicit(T x, T y)
115 		{
116 			return x + y; // expected-note {{function cannot be inferred 'nonblocking' because it calls non-'nonblocking' function}}
117 		}
118 	};
119 
120 	struct Stringy {
121 		friend Stringy operator+(const Stringy& x, const Stringy& y)
122 		{
123 			// Do something inferably unsafe
124 			auto* z = new char[42]; // expected-note {{function cannot be inferred 'nonblocking' because it allocates or deallocates memory}}
125 			return {};
126 		}
127 	};
128 
129 	struct Stringy2 {
130 		friend Stringy2 operator+(const Stringy2& x, const Stringy2& y)
131 		{
132 			// Do something inferably unsafe
133 			throw 42; // expected-note {{function cannot be inferred 'nonblocking' because it throws or catches exceptions}}
134 		}
135 	};
136 
137 void nb9() [[clang::nonblocking]]
138 {
139 	Adder<int>::add_explicit(1, 2);
140 	Adder<int>::add_implicit(1, 2);
141 
142 	Adder<Stringy>::add_explicit({}, {}); // expected-note {{in template expansion here}}
143 	Adder<Stringy2>::add_implicit({}, {}); // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' function}} \
144 		expected-note {{in template expansion here}}
145 }
146 
147 // Make sure we verify lambdas produced from template expansions.
148 struct HasTemplatedLambda {
149 	void (*fptr)() [[clang::nonblocking]];
150 
151 	template <typename C>
152 	HasTemplatedLambda(const C&)
153 		: fptr{ []() [[clang::nonblocking]] {
154 			auto* y = new int; // expected-warning {{lambda with 'nonblocking' attribute must not allocate or deallocate memory}}
155 		} }
156 	{}
157 };
158 
159 void nb9a()
160 {
161 	HasTemplatedLambda bad(42);
162 }
163 
164 // Templated function and lambda.
165 template <typename T>
166 void TemplatedFunc(T x) [[clang::nonblocking]] {
167 	auto* ptr = new T; // expected-warning {{function with 'nonblocking' attribute must not allocate or deallocate memory}}
168 }
169 
170 void nb9b() [[clang::nonblocking]] {
171 	TemplatedFunc(42); // expected-note {{in template expansion here}}
172 
173 	auto foo = [](auto x) [[clang::nonblocking]] {
174 		auto* ptr = new int; // expected-warning {{lambda with 'nonblocking' attribute must not allocate or deallocate memory}}
175 		return x;
176 	};
177 
178 	// Note that foo() won't be validated unless instantiated.
179 	foo(42);
180 }
181 
182 void nb10(
183 	void (*fp1)(), // expected-note {{function pointer cannot be inferred 'nonblocking'}}
184 	void (*fp2)() [[clang::nonblocking]]
185 	) [[clang::nonblocking]]
186 {
187 	fp1(); // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' function}}
188 	fp2();
189 
190 	// When there's a cast, there's a separate diagnostic.
191 	static_cast<void (*)()>(fp1)(); // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' expression}}
192 }
193 
194 // Expression involving indirection
195 int nb10a() [[clang::nonblocking]];
196 int nb10b() [[clang::nonblocking]];
197 int blocking();
198 
199 int nb10c(bool x) [[clang::nonblocking]]
200 {
201 	int y = (x ? nb10a : blocking)(); // expected-warning {{attribute 'nonblocking' should not be added via type conversion}}
202 	return (x ? nb10a : nb10b)(); // No diagnostic.
203 }
204 
205 // Interactions with nonblocking(false)
206 void nb11_no_inference_1() [[clang::nonblocking(false)]] // expected-note {{function does not permit inference of 'nonblocking'}}
207 {
208 }
209 void nb11_no_inference_2() [[clang::nonblocking(false)]]; // expected-note {{function does not permit inference of 'nonblocking'}}
210 
211 template <bool V>
212 struct ComputedNB {
213 	void method() [[clang::nonblocking(V)]]; // expected-note {{function does not permit inference of 'nonblocking' because it is declared 'blocking'}}
214 };
215 
216 void nb11() [[clang::nonblocking]]
217 {
218 	nb11_no_inference_1(); // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' function}}
219 	nb11_no_inference_2(); // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' function}}
220 
221 	ComputedNB<true> CNB_true;
222 	CNB_true.method();
223 
224 	ComputedNB<false> CNB_false;
225 	CNB_false.method(); // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' function}}
226 }
227 
228 // Verify that when attached to a redeclaration, the attribute successfully attaches.
229 void nb12() {
230 	static int x; // expected-warning {{function with 'nonblocking' attribute must not have static local variables}}
231 }
232 void nb12() [[clang::nonblocking]];
233 void nb13() [[clang::nonblocking]] { nb12(); }
234 
235 // C++ member function pointers
236 struct PTMFTester {
237 	typedef void (PTMFTester::*ConvertFunction)() [[clang::nonblocking]];
238 
239 	void convert() [[clang::nonblocking]];
240 
241 	ConvertFunction mConvertFunc;
242 };
243 
244 void PTMFTester::convert() [[clang::nonblocking]]
245 {
246 	(this->*mConvertFunc)();
247 }
248 
249 // Allow implicit conversion from array to pointer.
250 void nb14(unsigned idx) [[clang::nonblocking]]
251 {
252 	using FP = void (*)() [[clang::nonblocking]];
253 	using FPArray = FP[2];
254 	auto nb = +[]() [[clang::nonblocking]] {};
255 
256 	FPArray src{ nb, nullptr };
257 	FP f = src[idx]; // This should not generate a warning.
258 
259 	FP twoDim[2][2] = {};
260 	FP g = twoDim[1][1];
261 
262 	FP vla[idx];
263 	FP h = vla[0];
264 }
265 
266 // Block variables
267 void nb17(void (^blk)() [[clang::nonblocking]]) [[clang::nonblocking]] {
268 	blk();
269 }
270 
271 // References to blocks
272 void nb18(void (^block)() [[clang::nonblocking]]) [[clang::nonblocking]]
273 {
274 	auto &ref = block;
275 	ref();
276 }
277 
278 // Builtin functions
279 void nb19() [[clang::nonblocking]] {
280 	__builtin_assume(1);
281 	void *ptr = __builtin_malloc(1); // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' function '__builtin_malloc'}}
282 	__builtin_free(ptr); // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' function '__builtin_free'}}
283 
284 	void *p2 = __builtin_operator_new(1); // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' function '__builtin_operator_new'}}
285 	__builtin_operator_delete(p2); // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' function '__builtin_operator_delete'}}
286 }
287 
288 // Function try-block
289 void catches() try {} catch (...) {} // expected-note {{function cannot be inferred 'nonblocking' because it throws or catches exceptions}}
290 
291 void nb20() [[clang::nonblocking]] {
292 	catches(); // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' function 'catches'}}
293 }
294 
295 struct S {
296     int x;
297     S(int x) try : x(x) {} catch (...) {} // expected-note {{constructor cannot be inferred 'nonblocking' because it throws or catches exceptions}}
298     S(double) : x((throw 3, 3)) {} // expected-note {{member initializer cannot be inferred 'nonblocking' because it throws or catches exceptions}} \
299                                       expected-note {{in constructor here}}
300 };
301 
302 int badi(); // expected-note {{declaration cannot be inferred 'nonblocking' because it has no definition in this translation unit}} \
303             // expected-note {{declaration cannot be inferred 'nonblocking' because it has no definition in this translation unit}}
304 
305 struct A {                // expected-note {{in implicit constructor here}}
306     int x = (throw 3, 3); // expected-note {{member initializer cannot be inferred 'nonblocking' because it throws or catches exceptions}}
307 };
308 
309 struct B {
310     int y = badi(); // expected-note {{member initializer cannot be inferred 'nonblocking' because it calls non-'nonblocking' function 'badi'}}
311 };
312 
313 void f() [[clang::nonblocking]] {
314     S s1(3);   // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' constructor 'S::S'}}
315     S s2(3.0); // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' constructor 'S::S'}}
316     A a;       // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' constructor 'A::A'}}
317     B b;       // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' constructor 'B::B'}}
318 }
319 
320 struct T {
321 	int x = badi();               // expected-warning {{member initializer of constructor with 'nonblocking' attribute must not call non-'nonblocking' function 'badi'}}
322 	T() [[clang::nonblocking]] {} // expected-note {{in constructor here}}
323 	T(int x) [[clang::nonblocking]] : x(x) {} // OK
324 };
325 
326 // Default arguments
327 int badForDefaultArg(); // expected-note {{declaration cannot be inferred 'nonblocking' because it has no definition in this translation unit}} \
328                            expected-note {{declaration cannot be inferred 'nonblocking' because it has no definition in this translation unit}} \
329 						   expected-note {{declaration cannot be inferred 'nonblocking' because it has no definition in this translation unit}}
330 
331 void hasDefaultArg(int param = badForDefaultArg()) { // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' function 'badForDefaultArg'}} \
332                                                         expected-note {{function cannot be inferred 'nonblocking' because it calls non-'nonblocking' function 'badForDefaultArg'}}
333 }
334 
335 void nb21() [[clang::nonblocking]] {
336 	hasDefaultArg(); // expected-note {{in evaluating default argument here}} \
337 	                    expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' function 'hasDefaultArg'}}
338 }
339 
340 void nb22(int param = badForDefaultArg()) [[clang::nonblocking]] { // expected-warning {{function with 'nonblocking' attribute must not call non-'nonblocking' function 'badForDefaultArg'}}
341 }
342 
343 // Verify traversal of implicit code paths - constructors and destructors.
344 struct Unsafe {
345   static void problem1();   // expected-note {{declaration cannot be inferred 'nonblocking' because it has no definition in this translation unit}}
346   static void problem2();   // expected-note {{declaration cannot be inferred 'nonblocking' because it has no definition in this translation unit}}
347 
348   Unsafe() { problem1(); }  // expected-note {{constructor cannot be inferred 'nonblocking' because it calls non-'nonblocking' function 'Unsafe::problem1'}}
349   ~Unsafe() { problem2(); } // expected-note {{destructor cannot be inferred 'nonblocking' because it calls non-'nonblocking' function 'Unsafe::problem2'}}
350 
351   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}}
352 
353   // Delegating initializer.
354   Unsafe(float y) [[clang::nonblocking]] : Unsafe(int(y)) {} // expected-warning {{constructor with 'nonblocking' attribute must not call non-'nonblocking' constructor 'Unsafe::Unsafe'}}
355 };
356 
357 struct DerivedFromUnsafe : public Unsafe {
358   DerivedFromUnsafe() [[clang::nonblocking]] {} // expected-warning {{constructor with 'nonblocking' attribute must not call non-'nonblocking' constructor 'Unsafe::Unsafe'}}
359   DerivedFromUnsafe(int x) [[clang::nonblocking]] : Unsafe(x) {} // expected-warning {{constructor with 'nonblocking' attribute must not call non-'nonblocking' constructor 'Unsafe::Unsafe'}}
360   ~DerivedFromUnsafe() [[clang::nonblocking]] {} // expected-warning {{destructor with 'nonblocking' attribute must not call non-'nonblocking' destructor 'Unsafe::~Unsafe'}}
361 };
362 
363 // Don't try to follow a deleted destructor, as with std::optional<T>.
364 struct HasDtor {
365 	~HasDtor() {}
366 };
367 
368 template <typename T>
369 struct Optional {
370 	union {
371 		char __null_state_;
372 		T __val_;
373 	};
374 	bool engaged = false;
375 
376 	~Optional() {
377 		if (engaged)
378 			__val_.~T();
379 	}
380 };
381 
382 void nb_opt() [[clang::nonblocking]] {
383 	Optional<HasDtor> x;
384 }
385 
386 // Virtual inheritance
387 struct VBase {
388   int *Ptr;
389 
390   VBase() { Ptr = new int; }       // expected-note {{constructor cannot be inferred 'nonblocking' because it allocates or deallocates memory}}
391   virtual ~VBase() { delete Ptr; } // expected-note {{virtual method cannot be inferred 'nonblocking'}}
392 };
393 
394 struct VDerived : virtual VBase {
395   VDerived() [[clang::nonblocking]] {} // expected-warning {{constructor with 'nonblocking' attribute must not call non-'nonblocking' constructor 'VBase::VBase'}}
396 
397   ~VDerived() [[clang::nonblocking]] {} // expected-warning {{destructor with 'nonblocking' attribute must not call non-'nonblocking' destructor 'VBase::~VBase'}}
398 };
399 
400 // Contexts where there is no function call, no diagnostic.
401 bool bad();
402 
403 template <bool>
404 requires requires { bad(); }
405 void g() [[clang::nonblocking]] {}
406 
407 void g() [[clang::nonblocking]] {
408     decltype(bad()) a; // doesn't generate a call so, OK
409     [[maybe_unused]] auto b = noexcept(bad());
410     [[maybe_unused]] auto c = sizeof(bad());
411 #pragma clang diagnostic push
412 #pragma clang diagnostic ignored "-Wassume"
413     [[assume(bad())]]; // never evaluated, but maybe still semantically questionable?
414 #pragma clang diagnostic pop
415 }
416 
417 // Make sure we are skipping concept requirements -- they can trigger an unexpected
418 // warning involving use of a function pointer (e.g. std::reverse_iterator::operator==
419 struct HasFoo { int foo() const { return 0; } };
420 
421 template <class A, class B>
422 inline bool compare(const A& a, const B& b)
423 	requires requires {
424 		a.foo();
425 	}
426 {
427 	return a.foo() == b.foo();
428 }
429 
430 void nb25() [[clang::nonblocking]] {
431 	HasFoo a, b;
432 	compare(a, b);
433 }
434 
435 // If the callee is both noreturn and noexcept, it presumably terminates.
436 // Ignore it for the purposes of effect analysis.
437 [[noreturn]] void abort_wrapper() noexcept;
438 
439 void nb26() [[clang::nonblocking]] {
440 	abort_wrapper(); // no diagnostic
441 }
442 
443 // --- Make sure we don't traverse requires and noexcept clauses. ---
444 
445 // Apparently some requires clauses are able to be collapsed into a constant before the nonblocking
446 // analysis sees any function calls. This example (extracted from a real-world case where
447 // `operator&&` in <valarray>, preceding the inclusion of <expected>) is sufficiently complex
448 // to look like it contains function calls. There may be simpler examples.
449 
450 namespace ExpectedTest {
451 
452 template <class _Tp>
453 inline constexpr bool is_copy_constructible_v = __is_constructible(_Tp, _Tp&);
454 
455 template <bool, class _Tp = void>
456 struct enable_if {};
457 template <class _Tp>
458 struct enable_if<true, _Tp> {
459   typedef _Tp type;
460 };
461 
462 template <bool _Bp, class _Tp = void>
463 using enable_if_t = typename enable_if<_Bp, _Tp>::type;
464 
465 // Doesn't seem to matter whether the enable_if is true or false.
466 template <class E1, class E2, enable_if_t<is_copy_constructible_v<E1>> = 0>
467 inline bool operator&&(const E1& x, const E2& y);
468 
469 template <class _Tp, class _Err>
470 class expected {
471 public:
472   constexpr expected()
473     {}
474 
475   // This is a deliberate corruption of the real implementation for simplicity.
476   constexpr expected(const expected&)
477     requires(is_copy_constructible_v<_Tp> && is_copy_constructible_v<_Err>)
478   = default;
479 };
480 
481 void test() [[clang::nonblocking]]
482 {
483 	expected<int, int> a;
484 	auto b = a;            // Copy constructor.
485 }
486 
487 } // namespace ExpectedTest
488 
489 // Make sure a function call in a noexcept() clause is ignored.
490 constexpr bool foo() [[clang::nonblocking(false)]] { return true; }
491 void nb27() noexcept(foo()) [[clang::nonblocking]] {}
492 
493 // Make sure that simple type traits don't cause violations.
494 void nb28() [[clang::nonblocking]] {
495 	bool x = __is_constructible(int, const int&);
496 }
497 
498 // --- nonblocking implies noexcept ---
499 #pragma clang diagnostic warning "-Wperf-constraint-implies-noexcept"
500 
501 void needs_noexcept() [[clang::nonblocking]] // expected-warning {{function with 'nonblocking' attribute should be declared noexcept}}
502 {
503 	auto lambda = []() [[clang::nonblocking]] {}; // expected-warning {{lambda with 'nonblocking' attribute should be declared noexcept}}
504 }
505