================
@@ -0,0 +1,141 @@
+// RUN: %clang_cc1 -fsyntax-only -fblocks -fcxx-exceptions -verify %s
+// RUN: %clang_cc1 -fsyntax-only -fblocks -verify -x c -std=c23 %s
+
+#if !__has_attribute(nonblocking)
+#error "the 'nonblocking' attribute is not available"
+#endif
+
+// --- ATTRIBUTE SYNTAX: SUBJECTS ---
+
+int nl_var [[clang::nonblocking]]; // expected-warning {{'nonblocking' only 
applies to function types; type here is 'int'}}
+struct nl_struct {} [[clang::nonblocking]]; // expected-warning {{attribute 
'nonblocking' is ignored, place it after "struct" to apply attribute to type 
declaration}}
+struct [[clang::nonblocking]] nl_struct2 {}; // expected-error {{'nonblocking' 
attribute cannot be applied to a declaration}}
+
+// Positive case
+typedef void (*fo)() [[clang::nonblocking]];
+void (*read_me_and_weep(
+  int val, void (*func)(int) [[clang::nonblocking]])
+  [[clang::nonblocking]]) (int)
+  [[clang::nonblocking]];
+
+// --- ATTRIBUTE SYNTAX: ARGUMENT COUNT ---
+void nargs_1() [[clang::nonblocking(1, 2)]];  // expected-error 
{{'nonblocking' attribute takes no more than 1 argument}}
+void nargs_2() [[clang::nonallocating(1, 2)]]; // expected-error 
{{'nonallocating' attribute takes no more than 1 argument}}
+void nargs_3() [[clang::blocking(1)]]; // expected-error {{'blocking' 
attribute takes no arguments}}
+void nargs_4() [[clang::allocating(1)]]; // expected-error {{'allocating' 
attribute takes no arguments}}
+
+// --- ATTRIBUTE SYNTAX: COMBINATIONS ---
+// Check invalid combinations of nonblocking/nonallocating attributes
+
+void nl_true_false_1() [[clang::nonblocking(true)]] [[clang::blocking]]; // 
expected-error {{'blocking' and 'nonblocking' attributes are not compatible}}
+void nl_true_false_2() [[clang::blocking]] [[clang::nonblocking(true)]]; // 
expected-error {{'nonblocking' and 'blocking' attributes are not compatible}}
+
+void na_true_false_1() [[clang::nonallocating(true)]] [[clang::allocating]]; 
// expected-error {{'allocating' and 'nonallocating' attributes are not 
compatible}}
+void na_true_false_2() [[clang::allocating]] [[clang::nonallocating(true)]]; 
// expected-error {{'nonallocating' and 'allocating' attributes are not 
compatible}}
+
+void nl_true_na_true_1() [[clang::nonblocking]] [[clang::nonallocating]];
+void nl_true_na_true_2() [[clang::nonallocating]] [[clang::nonblocking]];
+
+void nl_true_na_false_1() [[clang::nonblocking]] [[clang::allocating]]; // 
expected-error {{'allocating' and 'nonblocking' attributes are not compatible}}
+void nl_true_na_false_2() [[clang::allocating]] [[clang::nonblocking]]; // 
expected-error {{'nonblocking' and 'allocating' attributes are not compatible}}
+
+void nl_false_na_true_1() [[clang::blocking]] [[clang::nonallocating]];
+void nl_false_na_true_2() [[clang::nonallocating]] [[clang::blocking]];
+
+void nl_false_na_false_1() [[clang::blocking]] [[clang::allocating]];
+void nl_false_na_false_2() [[clang::allocating]] [[clang::blocking]];
+
+// --- TYPE CONVERSIONS ---
+
+void unannotated();
+void nonblocking() [[clang::nonblocking]];
+void nonallocating() [[clang::nonallocating]];
+void type_conversions()
+{
+  // It's fine to remove a performance constraint.
+  void (*fp_plain)();
+
+  fp_plain = nullptr;
+  fp_plain = unannotated;
+  fp_plain = nonblocking;
+  fp_plain = nonallocating;
+
+  // Adding/spoofing nonblocking is unsafe.
+  void (*fp_nonblocking)() [[clang::nonblocking]];
+  fp_nonblocking = nullptr;
+  fp_nonblocking = nonblocking;
+  fp_nonblocking = unannotated; // expected-warning {{attribute 'nonblocking' 
should not be added via type conversion}}
+  fp_nonblocking = nonallocating; // expected-warning {{attribute 
'nonblocking' should not be added via type conversion}}
+
+  // Adding/spoofing nonallocating is unsafe.
+  void (*fp_nonallocating)() [[clang::nonallocating]];
+  fp_nonallocating = nullptr;
+  fp_nonallocating = nonallocating;
+  fp_nonallocating = nonblocking; // no warning because nonblocking includes 
nonallocating fp_nonallocating = unannotated;
+  fp_nonallocating = unannotated; // expected-warning {{attribute 
'nonallocating' should not be added via type conversion}}
+}
+
+#ifdef __cplusplus
+// There was a bug: noexcept and nonblocking could be individually removed in 
conversion, but not both  
+void type_conversions_2()
+{
+  auto receives_fp = [](void (*fp)()) {
+  };
+  
+  auto ne = +[]() noexcept {};
+  auto nl = +[]() [[clang::nonblocking]] {};
+  auto nl_ne = +[]() noexcept [[clang::nonblocking]] {};
+  
+  receives_fp(ne);
+  receives_fp(nl);
+  receives_fp(nl_ne);
+}
+#endif
+
+// --- VIRTUAL METHODS ---
+// Attributes propagate to overridden methods, so no diagnostics except for 
conflicts.
+// Check this in the syntax tests too.
+#ifdef __cplusplus
+struct Base {
+  virtual void f1();
+  virtual void nonblocking() noexcept [[clang::nonblocking]];
+  virtual void nonallocating() noexcept [[clang::nonallocating]];
+  virtual void f2() [[clang::nonallocating]]; // expected-note {{previous 
declaration is here}}
+};
+
+struct Derived : public Base {
+  void f1() [[clang::nonblocking]] override;
+  void nonblocking() noexcept override;
+  void nonallocating() noexcept override;
+  void f2() [[clang::allocating]] override; // expected-warning {{effects 
conflict when merging declarations; kept 'allocating', discarded 
'nonallocating'}}
+};
+#endif // __cplusplus
+
+// --- REDECLARATIONS ---
+
+void f2();
+void f2() [[clang::nonblocking]]; // expected-note {{previous declaration is 
here}}
+void f2(); // expected-warning {{attribute 'nonblocking' on function does not 
match previous declaration}}
+// Note: we verify that the attribute is actually seen during the constraints 
tests.
+
+void f3() [[clang::blocking]]; // expected-note {{previous declaration is 
here}}
+void f3() [[clang::nonblocking]]; // expected-warning {{effects conflict when 
merging declarations; kept 'blocking', discarded 'nonblocking'}}
+
+// --- OVERLOADS ---
+#ifdef __cplusplus
+struct S {
+  void foo(); // expected-note {{previous declaration is here}}
+  void foo(); // expected-error {{class member cannot be redeclared}}
+};
+#endif // __cplusplus
+
+// --- COMPUTED NONBLOCKING ---
+void f4() [[clang::nonblocking(__builtin_memset)]] {} // expected-error 
{{nonblocking attribute requires an integer constant}}
+
+#ifdef __cplusplus
+// Unexpanded parameter pack
+template <bool ...val>
+void f5() [[clang::nonblocking(val /* NO ... here */)]] {} // expected-error 
{{expression contains unexpanded parameter pack 'val'}}
+
----------------
dougsonos wrote:

It's not quite implemented the way you suggested because the attributes are 
represented in the type; now when TreeTransform transforms the 
`FunctionProtoType`'s effects, any conditions are transformed, and a 
newly-extracted validation method is called.  I didn't quite know what to 
expect!  But this seems unsurprising:

```
/xwork/attr-nolock-wip.cpp:7:3: error: no matching function for call to 
'ambiguous'
    7 |   ambiguous<true>();
      |   ^~~~~~~~~~~~~~~
/xwork/attr-nolock-wip.cpp:4:6: note: candidate template ignored: substitution 
failure [with B = true]: 'blocking' and 'nonblocking' attributes are not 
compatible
    4 | void ambiguous() [[clang::nonblocking(B)]] [[clang::blocking]];
      | ~~~~ ^
1 error generated.
```


https://github.com/llvm/llvm-project/pull/84983
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to