================ @@ -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