aboutsummaryrefslogtreecommitdiff
path: root/clang/test/Sema/attr-nonblocking-constraints.cpp
blob: b26a94584369602e1ff3cd6aee0c032e606b063a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
// 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 <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}}
}

// Make sure we verify lambdas produced from template expansions.
struct HasTemplatedLambda {
	void (*fptr)() [[clang::nonblocking]];

	template <typename C>
	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 <typename T>
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<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)();
}

// 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<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
}

// --- 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 <valarray>, preceding the inclusion of <expected>) is sufficiently complex
// to look like it contains function calls. There may be simpler examples.

namespace ExpectedTest {

template <class _Tp>
inline constexpr bool is_copy_constructible_v = __is_constructible(_Tp, _Tp&);

template <bool, class _Tp = void>
struct enable_if {};
template <class _Tp>
struct enable_if<true, _Tp> {
  typedef _Tp type;
};

template <bool _Bp, class _Tp = void>
using enable_if_t = typename enable_if<_Bp, _Tp>::type;

// Doesn't seem to matter whether the enable_if is true or false.
template <class E1, class E2, enable_if_t<is_copy_constructible_v<E1>> = 0>
inline bool operator&&(const E1& x, const E2& y);

template <class _Tp, class _Err>
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<int, int> 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}}
}