[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -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]]; //
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -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]]; //
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
https://github.com/dougsonos edited 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -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]]; //
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -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]]; //
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -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]]; dougsonos wrote: done 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -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]]; //
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -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]]; //
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -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}} dougsonos wrote: done 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
https://github.com/dougsonos edited 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -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. dougsonos wrote: Conceptually, we have to err on the side of conservative correctness here: ``` struct Base { virtual void process() [[clang::nonblocking]]; }; struct Derived { void process() override; } // inherits the attribute // (we did discuss making this a
[clang] [compiler-rt] [compiler-rt] Realtime Sanitizer: Introduce RADSan backend (PR #92460)
dougsonos wrote: > Looking for weighing in on any of the code as it stands now, or the naming > question posed by MaskRay > [here](https://discourse.llvm.org/t/rfc-nolock-and-noalloc-attributes/76837/75), > so far there seems to be support for rtsan over radsan :) +1 for rtsan https://github.com/llvm/llvm-project/pull/92460 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -7525,25 +7525,21 @@ static Attr *getCCTypeAttr(ASTContext , ParsedAttr ) { std::optional Sema::ActOnEffectExpression(Expr *CondExpr, StringRef AttributeName) { - auto BadExpr = [&]() { -Diag(CondExpr->getExprLoc(), diag::err_attribute_argument_type) -<< ("'" + AttributeName.str() + "'") << AANT_ArgumentIntegerConstant -<< CondExpr->getSourceRange(); + if (DiagnoseUnexpandedParameterPack(CondExpr)) return std::nullopt; - }; - - if (CondExpr->isTypeDependent() || CondExpr->isValueDependent()) { -if (CondExpr->containsUnexpandedParameterPack()) - return BadExpr(); + if (CondExpr->isTypeDependent() || CondExpr->isValueDependent()) return FunctionEffectMode::Dependent; - } std::optional ConditionValue = CondExpr->getIntegerConstantExpr(Context); - if (!ConditionValue) -return BadExpr(); - return ConditionValue->getExtValue() ? FunctionEffectMode::True - : FunctionEffectMode::False; + if (!ConditionValue) { +Diag(CondExpr->getExprLoc(), diag::err_attribute_argument_type) +<< ("'" + AttributeName.str() + "'") << AANT_ArgumentIntegerConstant dougsonos wrote: I did that, with a FIXME about the inconsistency of that diagnostic. 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -7525,25 +7525,21 @@ static Attr *getCCTypeAttr(ASTContext , ParsedAttr ) { std::optional Sema::ActOnEffectExpression(Expr *CondExpr, StringRef AttributeName) { - auto BadExpr = [&]() { -Diag(CondExpr->getExprLoc(), diag::err_attribute_argument_type) -<< ("'" + AttributeName.str() + "'") << AANT_ArgumentIntegerConstant -<< CondExpr->getSourceRange(); + if (DiagnoseUnexpandedParameterPack(CondExpr)) return std::nullopt; - }; - - if (CondExpr->isTypeDependent() || CondExpr->isValueDependent()) { -if (CondExpr->containsUnexpandedParameterPack()) - return BadExpr(); + if (CondExpr->isTypeDependent() || CondExpr->isValueDependent()) return FunctionEffectMode::Dependent; - } std::optional ConditionValue = CondExpr->getIntegerConstantExpr(Context); - if (!ConditionValue) -return BadExpr(); - return ConditionValue->getExtValue() ? FunctionEffectMode::True - : FunctionEffectMode::False; + if (!ConditionValue) { +Diag(CondExpr->getExprLoc(), diag::err_attribute_argument_type) +<< ("'" + AttributeName.str() + "'") << AANT_ArgumentIntegerConstant dougsonos wrote: The difficulty here is: - this is a pre-existing diagnostic; I didn't see a need for a new one just for quotes - the other users of the pre-existing diagnostic use `<<` with a `ParsedAttr`, which apparently quotes the attribute (?), but this function can be called from `TreeTransform` (for template instantiation), where there is only a `FunctionEffect` (whose name is passed in). And then there are usages like: ``` Diag(AttrLoc, diag::err_attribute_argument_type) << "vector_size" << AANT_ArgumentIntegerConstant << SizeExpr->getSourceRange(); ``` , passing a string constant; it would appear that in this case the attribute isn't quoted, which seems inconsistent. 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
dougsonos wrote: > @dougsonos Just merged a pr that diagnoses unexpanded parameter packs when an > attribute is parsed, so could you double-check if that call to > `DiagnoseUnexpandedParameterPack` is still necessary? I just re-merged main to the branch and verified that the call to `DiagnoseUnexpandedParameterPack` is no longer needed. 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
https://github.com/dougsonos edited 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
https://github.com/dougsonos edited 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -4640,6 +4647,306 @@ class FunctionNoProtoType : public FunctionType, public llvm::FoldingSetNode { } }; +// -- + +/// Represents an abstract function effect, using just an enumeration describing +/// its kind. +class FunctionEffect { +public: + /// Identifies the particular effect. + enum class Kind : uint8_t { +None = 0, +NonBlocking = 1, +NonAllocating = 2, +Blocking = 3, +Allocating = 4 + }; + + /// Flags describing some behaviors of the effect. + using Flags = unsigned; + enum FlagBit : Flags { +// Can verification inspect callees' implementations? (e.g. nonblocking: +// yes, tcb+types: no). This also implies the need for 2nd-pass +// verification. +FE_InferrableOnCallees = 0x1, + +// Language constructs which effects can diagnose as disallowed. +FE_ExcludeThrow = 0x2, +FE_ExcludeCatch = 0x4, +FE_ExcludeObjCMessageSend = 0x8, +FE_ExcludeStaticLocalVars = 0x10, +FE_ExcludeThreadLocalVars = 0x20 + }; + +private: + LLVM_PREFERRED_TYPE(Kind) + unsigned FKind : 3; + + // Expansion: for hypothetical TCB+types, there could be one Kind for TCB, + // then ~16(?) bits "SubKind" to map to a specific named TCB. SubKind would + // be considered for uniqueness. + +public: + FunctionEffect() : FKind(unsigned(Kind::None)) {} + + explicit FunctionEffect(Kind K) : FKind(unsigned(K)) {} + + /// The kind of the effect. + Kind kind() const { return Kind(FKind); } + + /// Return the opposite kind, for effects which have opposites. + Kind oppositeKind() const; + + /// For serialization. + uint32_t toOpaqueInt32() const { return FKind; } + static FunctionEffect fromOpaqueInt32(uint32_t Value) { +return FunctionEffect(Kind(Value)); + } + + /// Flags describing some behaviors of the effect. + Flags flags() const { +switch (kind()) { +case Kind::NonBlocking: + return FE_InferrableOnCallees | FE_ExcludeThrow | FE_ExcludeCatch | + FE_ExcludeObjCMessageSend | FE_ExcludeStaticLocalVars | + FE_ExcludeThreadLocalVars; +case Kind::NonAllocating: + // Same as NonBlocking, except without FE_ExcludeStaticLocalVars. + return FE_InferrableOnCallees | FE_ExcludeThrow | FE_ExcludeCatch | + FE_ExcludeObjCMessageSend | FE_ExcludeThreadLocalVars; +case Kind::Blocking: +case Kind::Allocating: + return 0; +case Kind::None: + break; +} +llvm_unreachable("unknown effect kind"); + } + + /// The description printed in diagnostics, e.g. 'nonblocking'. + StringRef name() const; + + /// Return true if the effect is allowed to be inferred on the callee, + /// which is either a FunctionDecl or BlockDecl. + /// This is only used if the effect has FE_InferrableOnCallees flag set. + /// Example: This allows nonblocking(false) to prevent inference for the + /// function. + bool canInferOnFunction(const Decl ) const; + + // Return false for success. When true is returned for a direct call, then the + // FE_InferrableOnCallees flag may trigger inference rather than an immediate + // diagnostic. Caller should be assumed to have the effect (it may not have it + // explicitly when inferring). + bool shouldDiagnoseFunctionCall(bool Direct, + ArrayRef CalleeFX) const; + + friend bool operator==(const FunctionEffect , const FunctionEffect ) { +return LHS.FKind == RHS.FKind; + } + friend bool operator!=(const FunctionEffect , const FunctionEffect ) { +return !(LHS == RHS); + } + friend bool operator<(const FunctionEffect , const FunctionEffect ) { +return LHS.FKind < RHS.FKind; + } +}; + +/// Wrap a function effect's condition expression in another struct so +/// that FunctionProtoType's TrailingObjects can treat it separately. +class FunctionEffectCondition { dougsonos wrote: How about `EffectConditionExpr` : the struct holding an `Expr*` for disambiguation with trailing objects `FunctionEffectWithCondition` : combining `FunctionEffect` and `FunctionEffectWithCondition` ? 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
dougsonos wrote: I've been through all the feedback again and believe I have addressed everything. 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -10510,6 +10512,8 @@ QualType ASTContext::mergeFunctionTypes(QualType lhs, QualType rhs, if (lproto->getMethodQuals() != rproto->getMethodQuals()) return {}; +// TODO: (nonblocking) Does anything need to be done with FunctionEffects? + dougsonos wrote: This now creates a composite type using either the intersection or union of the effects, depending on context -- replicating the logic of `noreturn`. 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -7973,3 +7973,20 @@ requirement: } }]; } + +def NoLockNoAllocDocs : Documentation { + let Category = DocCatType; + let Content = [{ +The ``nolock`` and ``noalloc`` attributes can be attached to functions, blocks, +function pointers, lambdas, and member functions. The attributes identify code +which must not allocate memory or lock, and the compiler uses the attributes to +verify these requirements. + +Like ``noexcept``, ``nolock`` and ``noalloc`` have an optional argument, a +compile-time constant boolean expression. By default, the argument is true, so +``[[clang::nolock(true)]]`` is equivalent to ``[[clang::nolock]]``, and declares +the function type as never locking. + +TODO: how much of the RFC to include here? Make it a separate page? dougsonos wrote: done :) 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -4639,6 +4645,303 @@ class FunctionNoProtoType : public FunctionType, public llvm::FoldingSetNode { } }; +// -- + +/// Represents an abstract function effect, using just an enumeration describing +/// its kind. +class FunctionEffect { +public: + /// Identifies the particular effect. + enum class Kind : uint8_t { +None = 0, +NonBlocking = 1, +NonAllocating = 2, +Blocking = 3, +Allocating = 4 + }; + + /// Flags describing some behaviors of the effect. + using Flags = unsigned; + enum FlagBit : Flags { +// Can verification inspect callees' implementations? (e.g. nonblocking: +// yes, tcb+types: no). This also implies the need for 2nd-pass +// verification. +FE_InferrableOnCallees = 0x1, + +// Language constructs which effects can diagnose as disallowed. +FE_ExcludeThrow = 0x2, +FE_ExcludeCatch = 0x4, +FE_ExcludeObjCMessageSend = 0x8, +FE_ExcludeStaticLocalVars = 0x10, +FE_ExcludeThreadLocalVars = 0x20 + }; + +private: + LLVM_PREFERRED_TYPE(Kind) + unsigned FKind : 3; + + // Expansion: for hypothetical TCB+types, there could be one Kind for TCB, + // then ~16(?) bits "SubKind" to map to a specific named TCB. SubKind would + // be considered for uniqueness. + +public: + FunctionEffect() : FKind(unsigned(Kind::None)) {} + + explicit FunctionEffect(Kind K) : FKind(unsigned(K)) {} + + /// The kind of the effect. + Kind kind() const { return Kind(FKind); } + + /// Return the opposite kind, for effects which have opposites. + Kind oppositeKind() const; + + /// For serialization. + uint32_t toOpaqueInt32() const { return FKind; } + static FunctionEffect fromOpaqueInt32(uint32_t Value) { +return FunctionEffect(Kind(Value)); + } + + /// Flags describing some behaviors of the effect. + Flags flags() const { +switch (kind()) { +case Kind::NonBlocking: + return FE_InferrableOnCallees | FE_ExcludeThrow | FE_ExcludeCatch | + FE_ExcludeObjCMessageSend | FE_ExcludeStaticLocalVars | + FE_ExcludeThreadLocalVars; +case Kind::NonAllocating: + // Same as NonBlocking, except without FE_ExcludeStaticLocalVars + return FE_InferrableOnCallees | FE_ExcludeThrow | FE_ExcludeCatch | + FE_ExcludeObjCMessageSend | FE_ExcludeThreadLocalVars; +case Kind::Blocking: +case Kind::Allocating: + return 0; +case Kind::None: + break; +} +llvm_unreachable("unknown effect kind"); + } + + /// The description printed in diagnostics, e.g. 'nonblocking'. + StringRef name() const; + + /// Return true if the effect is allowed to be inferred on the callee, + /// which is either a FunctionDecl or BlockDecl. + /// This is only used if the effect has FE_InferrableOnCallees flag set. + /// Example: This allows nonblocking(false) to prevent inference for the + /// function. + bool canInferOnFunction(const Decl ) const; + + // Return false for success. When true is returned for a direct call, then the + // FE_InferrableOnCallees flag may trigger inference rather than an immediate + // diagnostic. Caller should be assumed to have the effect (it may not have it + // explicitly when inferring). + bool shouldDiagnoseFunctionCall(bool Direct, + ArrayRef CalleeFX) const; + + friend bool operator==(const FunctionEffect , const FunctionEffect ) { +return LHS.FKind == RHS.FKind; + } + friend bool operator!=(const FunctionEffect , const FunctionEffect ) { +return !(LHS == RHS); + } + friend bool operator<(const FunctionEffect , const FunctionEffect ) { +return LHS.FKind < RHS.FKind; + } +}; + +/// Wrap a function effect's condition expression in another struct so +/// that FunctionProtoType's TrailingObjects can treat it separately. +class FunctionEffectCondition { + Expr *Cond = nullptr; // if null, unconditional + +public: + FunctionEffectCondition() = default; + FunctionEffectCondition(Expr *E) : Cond(E) {} // implicit OK + + Expr *expr() const { return Cond; } + + bool operator==(const FunctionEffectCondition ) const { +return Cond == RHS.Cond; + } +}; + +/// A FunctionEffect plus a potential boolean expression determining whether +/// the effect is declared (e.g. nonblocking(expr)). Generally the condition +/// expression when present, is dependent. +struct FunctionEffectWithCondition { + FunctionEffect Effect; + FunctionEffectCondition Cond; + + FunctionEffectWithCondition() = default; + FunctionEffectWithCondition(const FunctionEffect , + const FunctionEffectCondition ) + : Effect(E), Cond(C) {} + + /// Return a textual description of the effect, and its condition, if any. + std::string description() const; + + friend bool operator<(const FunctionEffectWithCondition , +const FunctionEffectWithCondition ) { +if (LHS.Effect < RHS.Effect) +
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -4429,6 +4434,284 @@ class FunctionNoProtoType : public FunctionType, public llvm::FoldingSetNode { } }; +// -- + +class Decl; +class CXXMethodDecl; +class FunctionTypeEffectsRef; +class FunctionTypeEffectSet; + +/* + TODO: Idea about how to move most of the FunctionEffect business out of + Type.h, thus removing these forward declarations. + + - Keep FunctionEffect itself here but make it more minimal. Don't define flags + or any behaviors, just the Kind and an accessor. + - Keep FunctionEffectCondExpr here. + - Make FunctionProtoType and ExtProtoInfo use only ArrayRef + and ArrayRef. + - Somewhere in Sema, define ExtFunctionEffect, which holds a FunctionEffect +and has all the behavior-related methods. + - There too, define the containers. FunctionTypeEffectsRef can have a + constructor or factory method that initializes itself from a + FunctionProtoType. +*/ dougsonos wrote: All cleaned up now 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -4429,6 +4433,210 @@ class FunctionNoProtoType : public FunctionType, public llvm::FoldingSetNode { } }; +// -- + +class Decl; +class CXXMethodDecl; +class FunctionEffectSet; + +/// Represents an abstract function effect, using just an enumeration describing +/// its type. Encapsulates its semantic behaviors. +class FunctionEffect { +public: + /// Identifies the particular effect. + enum class Kind : uint8_t { +None, +NonBlocking, +NonAllocating, + }; + + /// Flags describing some behaviors of the effect. + using Flags = unsigned; + enum FlagBit : Flags { +// Can verification inspect callees' implementations? (e.g. nonblocking: +// yes, tcb+types: no) +FE_InferrableOnCallees = 0x1, + +// Language constructs which effects can diagnose as disallowed. +FE_ExcludeThrow = 0x2, +FE_ExcludeCatch = 0x4, +FE_ExcludeObjCMessageSend = 0x8, +FE_ExcludeStaticLocalVars = 0x10, +FE_ExcludeThreadLocalVars = 0x20 + }; + + /// Describes the result of effects differing between a base class's virtual + /// method and an overriding method in a subclass. + enum class OverrideResult { +Ignore, +Warn, +Merge // Base method's effects are merged with those of the override. + }; + +private: + Kind FKind; + + // Expansion: for hypothetical TCB+types, there could be one Kind for TCB, + // then ~16(?) bits "SubKind" to map to a specific named TCB. SubKind would + // be considered for uniqueness. + +public: + FunctionEffect() : FKind(Kind::None) {} + + explicit FunctionEffect(Kind T) : FKind(T) {} + + /// The kind of the effect. + Kind kind() const { return FKind; } + + /// Return an opaque integer, as a serializable representation. + uint32_t getAsOpaqueValue() const { return llvm::to_underlying(FKind); } + + /// Construct from a serialized representation. + static FunctionEffect getFromOpaqueValue(uint32_t V) { +return FunctionEffect(static_cast(V)); + } + + /// Flags describing some behaviors of the effect. + Flags flags() const { +switch (FKind) { +case Kind::NonBlocking: + return FE_InferrableOnCallees | FE_ExcludeThrow | FE_ExcludeCatch | + FE_ExcludeObjCMessageSend | FE_ExcludeStaticLocalVars | + FE_ExcludeThreadLocalVars; +case Kind::NonAllocating: + // Same as NonBlocking, except without FE_ExcludeStaticLocalVars + return FE_InferrableOnCallees | FE_ExcludeThrow | FE_ExcludeCatch | + FE_ExcludeObjCMessageSend | FE_ExcludeThreadLocalVars; +case Kind::None: + break; +} +llvm_unreachable("unknown effect kind"); + } + + /// The description printed in diagnostics, e.g. 'nonblocking'. + StringRef name() const; + + /// Return true if adding or removing the effect as part of a type conversion + /// should generate a diagnostic. + bool shouldDiagnoseConversion(bool Adding, QualType OldType, +const FunctionEffectSet , +QualType NewType, +const FunctionEffectSet ) const; + + /// Return true if adding or removing the effect in a redeclaration should + /// generate a diagnostic. + bool shouldDiagnoseRedeclaration(bool Adding, const FunctionDecl , + const FunctionEffectSet , + const FunctionDecl , + const FunctionEffectSet ) const; + + /// Return true if adding or removing the effect in a C++ virtual method + /// override should generate a diagnostic. + OverrideResult + shouldDiagnoseMethodOverride(bool Adding, const CXXMethodDecl , + const FunctionEffectSet , + const CXXMethodDecl , + const FunctionEffectSet ) const; + + /// Return true if the effect is allowed to be inferred on the callee, + /// which is either a FunctionDecl or BlockDecl. + /// This is only used if the effect has FE_InferrableOnCallees flag set. + /// Example: This allows nonblocking(false) to prevent inference for the + /// function. + bool canInferOnFunction(const Decl ) const; + + // Return false for success. When true is returned for a direct call, then the + // FE_InferrableOnCallees flag may trigger inference rather than an immediate + // diagnostic. Caller should be assumed to have the effect (it may not have it + // explicitly when inferring). + bool shouldDiagnoseFunctionCall(bool Direct, + const FunctionEffectSet ) const; + + friend bool operator==(const FunctionEffect , const FunctionEffect ) { +return LHS.FKind == RHS.FKind; + } + friend bool operator!=(const FunctionEffect , const FunctionEffect ) { +return LHS.FKind != RHS.FKind; + } + friend bool operator<(const FunctionEffect , const FunctionEffect ) { +return LHS.FKind <
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -17154,6 +17156,10 @@ ExprResult Sema::ActOnBlockStmtExpr(SourceLocation CaretLoc, BlockScopeInfo *BSI = cast(FunctionScopes.back()); BlockDecl *BD = BSI->TheDecl; + if (const auto FX = BD->getFunctionEffects()) { +CheckAddCallableWithEffects(BD, FX); + } dougsonos wrote: follow-on 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -1888,6 +1888,11 @@ ExprResult Sema::BuildCaptureInit(const Capture , ExprResult Sema::ActOnLambdaExpr(SourceLocation StartLoc, Stmt *Body) { LambdaScopeInfo LSI = *cast(FunctionScopes.back()); ActOnFinishFunctionBody(LSI.CallOperator, Body); + + if (const auto FX = LSI.CallOperator->getFunctionEffects()) { +CheckAddCallableWithEffects(LSI.CallOperator, FX); + } dougsonos wrote: will have to revisit this in the follow-on PR 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -4429,6 +4433,218 @@ class FunctionNoProtoType : public FunctionType, public llvm::FoldingSetNode { } }; +// -- + +// TODO: Should FunctionEffect be located elsewhere, where Decl is not +// forward-declared? +class Decl; +class CXXMethodDecl; +class FunctionEffectSet; +class TypeSourceInfo; + +/// Represents an abstract function effect. +class FunctionEffect { +public: + /// Identifies the particular type of effect. + enum class Type { +None = 0, +NonBlocking, +NonAllocating, + }; + + /// Flags describing behaviors of the effect. + // (Why not a struct with bitfields? There's one function that would like to + // test a caller-specified bit. There are some potential optimizations that + // would OR together the bits of multiple effects.) + using Flags = unsigned; + enum FlagBit : unsigned { +// Can verification inspect callees' implementations? (e.g. nonblocking: +// yes, tcb+types: no) +FE_InferrableOnCallees = 0x1, + +// Language constructs which effects can diagnose as disallowed. +FE_ExcludeThrow = 0x2, +FE_ExcludeCatch = 0x4, +FE_ExcludeObjCMessageSend = 0x8, +FE_ExcludeStaticLocalVars = 0x10, +FE_ExcludeThreadLocalVars = 0x20 + }; + + /// Describes the result of effects differing between a base class's virtual + /// method and an overriding method in a subclass. + enum class OverrideResult { +Ignore, +Warn, +Propagate // Base method's effects are merged with those of the override. + }; + +private: + // For uniqueness, currently only Type_ is significant. + + LLVM_PREFERRED_TYPE(Type) + unsigned Type_ : 2; + Flags Flags_ : 8; // A constant function of Type but cached here. + + // Expansion: for hypothetical TCB+types, there could be one Type for TCB, + // then ~16(?) bits "Subtype" to map to a specific named TCB. Subtype would + // be considered for uniqueness. + + // Since this struct is serialized as if it were a uint32_t, it's important + // to pad and explicitly zero the extra bits. + [[maybe_unused]] unsigned Padding : 22; + +public: + using CalleeDeclOrType = + llvm::PointerUnion; + + FunctionEffect() : Type_(unsigned(Type::None)), Flags_(0), Padding(0) {} + + explicit FunctionEffect(Type T); + + /// The type of the effect. + Type type() const { return Type(Type_); } + + /// Flags describing behaviors of the effect. + Flags flags() const { return Flags_; } + + /// The description printed in diagnostics, e.g. 'nonblocking'. + StringRef name() const; + + /// A hashable representation. + uint32_t opaqueRepr() const { return Type_ | (Flags_ << 2u); } + + /// Return true if adding or removing the effect as part of a type conversion + /// should generate a diagnostic. + bool diagnoseConversion(bool Adding, QualType OldType, + FunctionEffectSet OldFX, QualType NewType, + FunctionEffectSet NewFX) const; + + /// Return true if adding or removing the effect in a redeclaration should + /// generate a diagnostic. + bool diagnoseRedeclaration(bool Adding, const FunctionDecl , + FunctionEffectSet OldFX, + const FunctionDecl , + FunctionEffectSet NewFX) const; + + /// Return true if adding or removing the effect in a C++ virtual method + /// override should generate a diagnostic. + OverrideResult diagnoseMethodOverride(bool Adding, +const CXXMethodDecl , +FunctionEffectSet OldFX, +const CXXMethodDecl , +FunctionEffectSet NewFX) const; + + /// Return true if the effect is allowed to be inferred on a Decl of the + /// specified type (generally a FunctionProtoType but TypeSourceInfo is + /// provided so any AttributedType sugar can be examined). TSI can be null + /// on an implicit function like a default constructor. + /// + /// This is only used if the effect has FE_InferrableOnCallees flag set. + /// Example: This allows nonblocking(false) to prevent inference for the + /// function. + bool canInferOnFunction(QualType QT, const TypeSourceInfo *FType) const; + + // Return false for success. When true is returned for a direct call, then the + // FE_InferrableOnCallees flag may trigger inference rather than an immediate + // diagnostic. Caller should be assumed to have the effect (it may not have it + // explicitly when inferring). + bool diagnoseFunctionCall(bool Direct, FunctionEffectSet CalleeFX) const; + + friend bool operator==(const FunctionEffect , const FunctionEffect ) { +return LHS.Type_ == RHS.Type_; + } + friend bool operator!=(const FunctionEffect , const FunctionEffect ) { +return LHS.Type_ != RHS.Type_; + } + friend bool operator<(const FunctionEffect , const
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -5016,3 +5024,254 @@ void AutoType::Profile(llvm::FoldingSetNodeID , const ASTContext ) { Profile(ID, Context, getDeducedType(), getKeyword(), isDependentType(), getTypeConstraintConcept(), getTypeConstraintArguments()); } + +FunctionEffect::FunctionEffect(Type T) +: Type_(static_cast(T)), Flags_(0), Padding(0) { + switch (T) { + case Type::NonBlocking: +Flags_ = FE_InferrableOnCallees | FE_ExcludeThrow | FE_ExcludeCatch | + FE_ExcludeObjCMessageSend | FE_ExcludeStaticLocalVars | + FE_ExcludeThreadLocalVars; +break; + + case Type::NonAllocating: +// Same as NonBlocking, except without FE_ExcludeStaticLocalVars +Flags_ = FE_InferrableOnCallees | FE_ExcludeThrow | FE_ExcludeCatch | + FE_ExcludeObjCMessageSend | FE_ExcludeThreadLocalVars; +break; + default: +break; + } +} + +StringRef FunctionEffect::name() const { + switch (type()) { + default: +return ""; + case Type::NonBlocking: +return "nonblocking"; + case Type::NonAllocating: +return "nonallocating"; + } +} + +bool FunctionEffect::diagnoseConversion(bool Adding, QualType OldType, +FunctionEffectSet OldFX, +QualType NewType, +FunctionEffectSet NewFX) const { + + switch (type()) { + case Type::NonAllocating: +// nonallocating can't be added (spoofed) during a conversion, unless we +// have nonblocking +if (Adding) { + for (const auto : OldFX) { +if (Effect.type() == Type::NonBlocking) + return false; + } +} +[[fallthrough]]; + case Type::NonBlocking: +// nonblocking can't be added (spoofed) during a conversion +return Adding; + default: +break; + } + return false; +} + +bool FunctionEffect::diagnoseRedeclaration(bool Adding, + const FunctionDecl , + FunctionEffectSet OldFX, + const FunctionDecl , + FunctionEffectSet NewFX) const { + switch (type()) { + case Type::NonAllocating: + case Type::NonBlocking: +// nonblocking/nonallocating can't be removed in a redeclaration +// adding -> false, removing -> true (diagnose) +return !Adding; + default: +break; + } + return false; +} + +FunctionEffect::OverrideResult FunctionEffect::diagnoseMethodOverride( +bool Adding, const CXXMethodDecl , FunctionEffectSet OldFX, +const CXXMethodDecl , FunctionEffectSet NewFX) const { + switch (type()) { + case Type::NonAllocating: + case Type::NonBlocking: +// if added on an override, that's fine and not diagnosed. +// if missing from an override (removed), propagate from base to derived. +return Adding ? OverrideResult::Ignore : OverrideResult::Propagate; + default: +break; + } + return OverrideResult::Ignore; +} + +bool FunctionEffect::canInferOnFunction(QualType QT, +const TypeSourceInfo *FType) const { + switch (type()) { + case Type::NonAllocating: + case Type::NonBlocking: { +// Does the sugar have nonblocking(false) / nonallocating(false) ? +if (QT->hasAttr(type() == Type::NonBlocking ? attr::Kind::Blocking +: attr::Kind::Allocating)) { + return false; +} + +return true; + } + + default: +break; + } + return false; +} + +bool FunctionEffect::diagnoseFunctionCall(bool Direct, + FunctionEffectSet CalleeFX) const { + switch (type()) { + case Type::NonAllocating: + case Type::NonBlocking: { +const Type CallerType = type(); +for (const auto : CalleeFX) { + const Type ET = Effect.type(); + // Does callee have same or stronger constraint? + if (ET == CallerType || + (CallerType == Type::NonAllocating && ET == Type::NonBlocking)) { +return false; // no diagnostic + } +} +return true; // warning + } + default: +break; + } + return false; +} + +// = + +MutableFunctionEffectSet::MutableFunctionEffectSet( +const FunctionEffect ) { + push_back(effect); +} + +void MutableFunctionEffectSet::insert(const FunctionEffect ) { + const auto = std::lower_bound(begin(), end(), Effect); + if (Iter == end() || *Iter != Effect) { +insert(Iter, Effect); + } +} + +MutableFunctionEffectSet & +MutableFunctionEffectSet::operator|=(FunctionEffectSet RHS) { + // TODO: For large RHS sets, use set_union or a custom insert-in-place + for (const auto : RHS) { +insert(Effect); + } + return *this; +} + +FunctionEffectSet FunctionEffectSet::getUnion(ASTContext , + const FunctionEffectSet , + const FunctionEffectSet ) { + // Optimize for one of the two
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -3144,6 +3154,9 @@ class Sema final { QualType T, TypeSourceInfo *TSInfo, StorageClass SC); + /// Potentially add a FunctionDecl or BlockDecl to DeclsWithEffectsToVerify. + void CheckAddCallableWithEffects(const Decl *D, FunctionEffectSet FX); dougsonos wrote: this is now part of the follow-on PR 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -352,6 +352,9 @@ let Class = FunctionProtoType in { def : Property<"AArch64SMEAttributes", UInt32> { let Read = [{ node->getAArch64SMEAttributes() }]; } + def : Property<"functionEffects", Array> { +let Read = [{ node->getFunctionEffects().serializable() }]; + } dougsonos wrote: done 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -4429,6 +4433,218 @@ class FunctionNoProtoType : public FunctionType, public llvm::FoldingSetNode { } }; +// -- + +// TODO: Should FunctionEffect be located elsewhere, where Decl is not +// forward-declared? +class Decl; +class CXXMethodDecl; +class FunctionEffectSet; +class TypeSourceInfo; + +/// Represents an abstract function effect. +class FunctionEffect { +public: + /// Identifies the particular type of effect. + enum class Type { +None = 0, +NonBlocking, +NonAllocating, + }; + + /// Flags describing behaviors of the effect. + // (Why not a struct with bitfields? There's one function that would like to + // test a caller-specified bit. There are some potential optimizations that + // would OR together the bits of multiple effects.) + using Flags = unsigned; + enum FlagBit : unsigned { +// Can verification inspect callees' implementations? (e.g. nonblocking: +// yes, tcb+types: no) +FE_InferrableOnCallees = 0x1, + +// Language constructs which effects can diagnose as disallowed. +FE_ExcludeThrow = 0x2, +FE_ExcludeCatch = 0x4, +FE_ExcludeObjCMessageSend = 0x8, +FE_ExcludeStaticLocalVars = 0x10, +FE_ExcludeThreadLocalVars = 0x20 + }; + + /// Describes the result of effects differing between a base class's virtual + /// method and an overriding method in a subclass. + enum class OverrideResult { +Ignore, +Warn, +Propagate // Base method's effects are merged with those of the override. + }; + +private: + // For uniqueness, currently only Type_ is significant. + + LLVM_PREFERRED_TYPE(Type) + unsigned Type_ : 2; + Flags Flags_ : 8; // A constant function of Type but cached here. + + // Expansion: for hypothetical TCB+types, there could be one Type for TCB, + // then ~16(?) bits "Subtype" to map to a specific named TCB. Subtype would + // be considered for uniqueness. + + // Since this struct is serialized as if it were a uint32_t, it's important + // to pad and explicitly zero the extra bits. + [[maybe_unused]] unsigned Padding : 22; + +public: + using CalleeDeclOrType = + llvm::PointerUnion; + + FunctionEffect() : Type_(unsigned(Type::None)), Flags_(0), Padding(0) {} + + explicit FunctionEffect(Type T); + + /// The type of the effect. + Type type() const { return Type(Type_); } + + /// Flags describing behaviors of the effect. + Flags flags() const { return Flags_; } + + /// The description printed in diagnostics, e.g. 'nonblocking'. + StringRef name() const; + + /// A hashable representation. + uint32_t opaqueRepr() const { return Type_ | (Flags_ << 2u); } + + /// Return true if adding or removing the effect as part of a type conversion + /// should generate a diagnostic. + bool diagnoseConversion(bool Adding, QualType OldType, + FunctionEffectSet OldFX, QualType NewType, + FunctionEffectSet NewFX) const; dougsonos wrote: This ended up being a method on FunctionEffectDiff in Sema.h 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -464,6 +466,16 @@ class ASTContext : public RefCountedBase { /// This is the top-level (C++20) Named module we are building. Module *CurrentCXXNamedModule = nullptr; + class FunctionEffectSetUniquing { +llvm::DenseSet> Set; + + public: +FunctionEffectSet getUniqued(llvm::ArrayRef FX); + +~FunctionEffectSetUniquing(); + }; + FunctionEffectSetUniquing UniquedFunctionEffectSet; + dougsonos wrote: The uniquing went away 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -4912,3 +4922,279 @@ void AutoType::Profile(llvm::FoldingSetNodeID , const ASTContext ) { Profile(ID, Context, getDeducedType(), getKeyword(), isDependentType(), getTypeConstraintConcept(), getTypeConstraintArguments()); } + +FunctionEffect::~FunctionEffect() = default; + +bool FunctionEffect::diagnoseConversion(bool Adding, QualType OldType, +FunctionEffectSet OldFX, +QualType NewType, +FunctionEffectSet NewFX) const { + return false; +} + +bool FunctionEffect::diagnoseRedeclaration(bool Adding, + const FunctionDecl , + FunctionEffectSet OldFX, + const FunctionDecl , + FunctionEffectSet NewFX) const { + return false; +} + +bool FunctionEffect::diagnoseMethodOverride(bool Adding, +const CXXMethodDecl , +FunctionEffectSet OldFX, +const CXXMethodDecl , +FunctionEffectSet NewFX) const { + return false; +} + +bool FunctionEffect::canInferOnDecl(const Decl *Caller, +FunctionEffectSet CallerFX) const { + return false; +} + +bool FunctionEffect::diagnoseFunctionCall(bool Direct, const Decl *Caller, + FunctionEffectSet CallerFX, + CalleeDeclOrType Callee, + FunctionEffectSet CalleeFX) const { + return false; +} + +const NoLockNoAllocEffect ::nolock_instance() { + static NoLockNoAllocEffect global(kNoLockTrue, "nolock"); + return global; +} + +const NoLockNoAllocEffect ::noalloc_instance() { + static NoLockNoAllocEffect global(kNoAllocTrue, "noalloc"); + return global; +} + +// TODO: Separate flags for noalloc +NoLockNoAllocEffect::NoLockNoAllocEffect(EffectType Ty, const char *Name) +: FunctionEffect(Ty, + kRequiresVerification | kVerifyCalls | + kInferrableOnCallees | kExcludeThrow | kExcludeCatch | + kExcludeObjCMessageSend | kExcludeStaticLocalVars | + kExcludeThreadLocalVars, + Name) {} + +NoLockNoAllocEffect::~NoLockNoAllocEffect() = default; + +std::string NoLockNoAllocEffect::attribute() const { + return std::string{"__attribute__((clang_"} + name().str() + "))"; +} + +bool NoLockNoAllocEffect::diagnoseConversion(bool Adding, QualType OldType, + FunctionEffectSet OldFX, + QualType NewType, + FunctionEffectSet NewFX) const { + // noalloc can't be added (spoofed) during a conversion, unless we have nolock + if (Adding) { +if (!isNoLock()) { + for (const auto *Effect : OldFX) { +if (Effect->type() == kNoLockTrue) + return false; + } +} +// nolock can't be added (spoofed) during a conversion. +return true; + } + return false; +} + +bool NoLockNoAllocEffect::diagnoseRedeclaration(bool Adding, +const FunctionDecl , +FunctionEffectSet OldFX, +const FunctionDecl , +FunctionEffectSet NewFX) const { + // nolock/noalloc can't be removed in a redeclaration + // adding -> false, removing -> true (diagnose) + return !Adding; +} + +bool NoLockNoAllocEffect::diagnoseMethodOverride( +bool Adding, const CXXMethodDecl , FunctionEffectSet OldFX, +const CXXMethodDecl , FunctionEffectSet NewFX) const { + // nolock/noalloc can't be removed from an override + return !Adding; +} + +bool NoLockNoAllocEffect::canInferOnDecl(const Decl *Caller, + FunctionEffectSet CallerFX) const { + // Does the Decl have nolock(false) / noalloc(false) ? + QualType QT; + if (isa(Caller)) { +const auto *TSI = cast(Caller)->getSignatureAsWritten(); +QT = TSI->getType(); + } else if (isa(Caller)) { +QT = cast(Caller)->getType(); + } else { +return false; + } + if (QT->hasAttr(isNoLock() ? attr::Kind::NoLock : attr::Kind::NoAlloc)) { +return false; + } + + return true; +} + +// TODO: Notice that we don't care about some of the parameters. Is the +// interface overly general? +bool NoLockNoAllocEffect::diagnoseFunctionCall( +bool Direct, const Decl *Caller, FunctionEffectSet CallerFX, +CalleeDeclOrType Callee, FunctionEffectSet CalleeFX) const { + const EffectType CallerType = type(); + for (const auto *Effect : CalleeFX) { +
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -7963,6 +7967,148 @@ static Attr *getCCTypeAttr(ASTContext , ParsedAttr ) { llvm_unreachable("unexpected attribute kind!"); } +ExprResult Sema::ActOnEffectExpression(Expr *CondExpr, FunctionEffectMode , + bool RequireConstexpr) { + // see checkFunctionConditionAttr, Sema::CheckCXXBooleanCondition + if (RequireConstexpr || !CondExpr->isTypeDependent()) { +ExprResult E = PerformContextuallyConvertToBool(CondExpr); +if (E.isInvalid()) + return E; +CondExpr = E.get(); +if (RequireConstexpr || !CondExpr->isValueDependent()) { + llvm::APSInt CondInt; + E = VerifyIntegerConstantExpression( + E.get(), , + // TODO: have our own diagnostic + diag::err_constexpr_if_condition_expression_is_not_constant); + if (E.isInvalid()) { +return E; + } + Mode = + (CondInt != 0) ? FunctionEffectMode::True : FunctionEffectMode::False; +} else { + Mode = FunctionEffectMode::Dependent; +} + } else { +Mode = FunctionEffectMode::Dependent; + } + return CondExpr; +} + +static bool +handleNonBlockingNonAllocatingTypeAttr(TypeProcessingState , + ParsedAttr , QualType , + FunctionTypeUnwrapper ) { + // Delay if this is not a function type. + if (!Unwrapped.isFunctionType()) +return false; + + // Require FunctionProtoType + auto *FPT = Unwrapped.get()->getAs(); + if (FPT == nullptr) { +// TODO: special diagnostic? dougsonos wrote: done 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -7963,6 +7967,148 @@ static Attr *getCCTypeAttr(ASTContext , ParsedAttr ) { llvm_unreachable("unexpected attribute kind!"); } +ExprResult Sema::ActOnEffectExpression(Expr *CondExpr, FunctionEffectMode , + bool RequireConstexpr) { + // see checkFunctionConditionAttr, Sema::CheckCXXBooleanCondition + if (RequireConstexpr || !CondExpr->isTypeDependent()) { +ExprResult E = PerformContextuallyConvertToBool(CondExpr); +if (E.isInvalid()) + return E; +CondExpr = E.get(); +if (RequireConstexpr || !CondExpr->isValueDependent()) { + llvm::APSInt CondInt; + E = VerifyIntegerConstantExpression( + E.get(), , + // TODO: have our own diagnostic + diag::err_constexpr_if_condition_expression_is_not_constant); dougsonos wrote: done 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
https://github.com/dougsonos edited 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -7963,6 +7967,148 @@ static Attr *getCCTypeAttr(ASTContext , ParsedAttr ) { llvm_unreachable("unexpected attribute kind!"); } +ExprResult Sema::ActOnEffectExpression(Expr *CondExpr, FunctionEffectMode , + bool RequireConstexpr) { + // see checkFunctionConditionAttr, Sema::CheckCXXBooleanCondition + if (RequireConstexpr || !CondExpr->isTypeDependent()) { +ExprResult E = PerformContextuallyConvertToBool(CondExpr); +if (E.isInvalid()) + return E; +CondExpr = E.get(); +if (RequireConstexpr || !CondExpr->isValueDependent()) { + llvm::APSInt CondInt; + E = VerifyIntegerConstantExpression( dougsonos wrote: `Sema::ActOnEffectExpression()` is now much simpler -- it checks for dependence and unexpanded parameter packs before trying to evaluate the expression as an integer constant. 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -7963,6 +7967,148 @@ static Attr *getCCTypeAttr(ASTContext , ParsedAttr ) { llvm_unreachable("unexpected attribute kind!"); } +ExprResult Sema::ActOnEffectExpression(Expr *CondExpr, FunctionEffectMode , + bool RequireConstexpr) { + // see checkFunctionConditionAttr, Sema::CheckCXXBooleanCondition + if (RequireConstexpr || !CondExpr->isTypeDependent()) { +ExprResult E = PerformContextuallyConvertToBool(CondExpr); dougsonos wrote: done 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -5028,3 +5050,376 @@ void AutoType::Profile(llvm::FoldingSetNodeID , const ASTContext ) { Profile(ID, Context, getDeducedType(), getKeyword(), isDependentType(), getTypeConstraintConcept(), getTypeConstraintArguments()); } + +FunctionEffect::Kind FunctionEffect::oppositeKind() const { + switch (kind()) { + case Kind::NonBlocking: +return Kind::Blocking; + case Kind::Blocking: +return Kind::NonBlocking; + case Kind::NonAllocating: +return Kind::Allocating; + case Kind::Allocating: +return Kind::NonAllocating; + case Kind::None: +return Kind::None; + } + llvm_unreachable("unknown effect kind"); +} + +StringRef FunctionEffect::name() const { + switch (kind()) { + case Kind::NonBlocking: +return "nonblocking"; + case Kind::NonAllocating: +return "nonallocating"; + case Kind::Blocking: +return "blocking"; + case Kind::Allocating: +return "allocating"; + case Kind::None: +break; + } + llvm_unreachable("unknown effect kind"); +} + +bool FunctionEffectDiff::shouldDiagnoseConversion( +QualType SrcType, const FunctionEffectsRef , QualType DstType, +const FunctionEffectsRef ) const { + + switch (EffectKind) { + case FunctionEffect::Kind::NonAllocating: +// nonallocating can't be added (spoofed) during a conversion, unless we +// have nonblocking +if (DiffKind == Kind::Added) { + for (const auto : SrcFX) { +if (CFE.Effect.kind() == FunctionEffect::Kind::NonBlocking) + return false; + } +} +[[fallthrough]]; + case FunctionEffect::Kind::NonBlocking: +// nonblocking can't be added (spoofed) during a conversion. +switch (DiffKind) { +case Kind::Added: + return true; +case Kind::Removed: + return false; +case Kind::ConditionMismatch: + return true; // TODO: ??? +} + case FunctionEffect::Kind::Blocking: + case FunctionEffect::Kind::Allocating: +return false; + case FunctionEffect::Kind::None: +break; + } + llvm_unreachable("unknown effect kind"); +} + +bool FunctionEffectDiff::shouldDiagnoseRedeclaration( +const FunctionDecl , const FunctionEffectsRef , +const FunctionDecl , const FunctionEffectsRef ) const { + switch (EffectKind) { + case FunctionEffect::Kind::NonAllocating: + case FunctionEffect::Kind::NonBlocking: +// nonblocking/nonallocating can't be removed in a redeclaration +switch (DiffKind) { +case Kind::Added: + return false; // No diagnostic. +case Kind::Removed: + return true; // Issue diagnostic +case Kind::ConditionMismatch: + // All these forms of mismatches are diagnosed. + return true; +} + case FunctionEffect::Kind::Blocking: + case FunctionEffect::Kind::Allocating: +return false; + case FunctionEffect::Kind::None: +break; + } + llvm_unreachable("unknown effect kind"); +} + +FunctionEffectDiff::OverrideResult +FunctionEffectDiff::shouldDiagnoseMethodOverride( +const CXXMethodDecl , const FunctionEffectsRef , +const CXXMethodDecl , const FunctionEffectsRef ) const { + switch (EffectKind) { + case FunctionEffect::Kind::NonAllocating: + case FunctionEffect::Kind::NonBlocking: +switch (DiffKind) { + +// If added on an override, that's fine and not diagnosed. +case Kind::Added: + return OverrideResult::NoAction; + +// If missing from an override (removed), propagate from base to derived. +case Kind::Removed: + return OverrideResult::Merge; + +// If there's a mismatch involving the effect's polarity or condition, +// issue a warning. +case Kind::ConditionMismatch: + return OverrideResult::Warn; +} + + case FunctionEffect::Kind::Blocking: + case FunctionEffect::Kind::Allocating: +return OverrideResult::NoAction; + + case FunctionEffect::Kind::None: +break; + } + llvm_unreachable("unknown effect kind"); +} + +bool FunctionEffect::canInferOnFunction(const Decl ) const { + switch (kind()) { + case Kind::NonAllocating: + case Kind::NonBlocking: { +FunctionEffectsRef CalleeFX; +if (auto *FD = Callee.getAsFunction()) + CalleeFX = FD->getFunctionEffects(); +else if (auto *BD = dyn_cast()) + CalleeFX = BD->getFunctionEffects(); +else + return false; +for (const FunctionEffectWithCondition : CalleeFX) { + // nonblocking/nonallocating cannot call allocating + if (CalleeEC.Effect.kind() == Kind::Allocating) +return false; + // nonblocking cannot call blocking + if (kind() == Kind::NonBlocking && + CalleeEC.Effect.kind() == Kind::Blocking) +return false; +} + } +return true; + + case Kind::Allocating: + case Kind::Blocking: +return false; + + case Kind::None: +break; + } + llvm_unreachable("unknown effect kind"); +} + +bool FunctionEffect::shouldDiagnoseFunctionCall( +bool Direct, ArrayRef CalleeFX) const { + switch (kind()) { + case Kind::NonAllocating: + case
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -5028,3 +5050,376 @@ void AutoType::Profile(llvm::FoldingSetNodeID , const ASTContext ) { Profile(ID, Context, getDeducedType(), getKeyword(), isDependentType(), getTypeConstraintConcept(), getTypeConstraintArguments()); } + +FunctionEffect::Kind FunctionEffect::oppositeKind() const { + switch (kind()) { + case Kind::NonBlocking: +return Kind::Blocking; + case Kind::Blocking: +return Kind::NonBlocking; + case Kind::NonAllocating: +return Kind::Allocating; + case Kind::Allocating: +return Kind::NonAllocating; + case Kind::None: +return Kind::None; + } + llvm_unreachable("unknown effect kind"); +} + +StringRef FunctionEffect::name() const { + switch (kind()) { + case Kind::NonBlocking: +return "nonblocking"; + case Kind::NonAllocating: +return "nonallocating"; + case Kind::Blocking: +return "blocking"; + case Kind::Allocating: +return "allocating"; + case Kind::None: +break; + } + llvm_unreachable("unknown effect kind"); +} + +bool FunctionEffectDiff::shouldDiagnoseConversion( +QualType SrcType, const FunctionEffectsRef , QualType DstType, +const FunctionEffectsRef ) const { + + switch (EffectKind) { + case FunctionEffect::Kind::NonAllocating: +// nonallocating can't be added (spoofed) during a conversion, unless we +// have nonblocking +if (DiffKind == Kind::Added) { + for (const auto : SrcFX) { +if (CFE.Effect.kind() == FunctionEffect::Kind::NonBlocking) + return false; + } +} +[[fallthrough]]; + case FunctionEffect::Kind::NonBlocking: +// nonblocking can't be added (spoofed) during a conversion. +switch (DiffKind) { +case Kind::Added: + return true; +case Kind::Removed: + return false; +case Kind::ConditionMismatch: + return true; // TODO: ??? dougsonos wrote: There are better comments around all of these now. 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -1870,6 +1870,28 @@ bool Sema::IsFunctionConversion(QualType FromType, QualType ToType, FromFn = QT->getAs(); Changed = true; } + +// For C, when called from checkPointerTypesForAssignment, +// we need not to alter FromFn, or else even an innocuous cast +// like dropping effects will fail. In C++ however we do want to +// alter FromFn. TODO: Is this correct? dougsonos wrote: The test case which exposes a need for this kind of logic is: ``` void nonblocking() [[clang::nonblocking]]; void type_conversions() { void (*fp_plain)(); fp_plain = nonblocking; } ``` The call chain to `IsFunctionConversion` is: ``` CheckSingleAssignmentConversion PerformImplicitConversion (in C++ only, not plain C) TryImplicitConversion IsStandardConversion IsFunctionConversion ``` `IsFunctionConversion` drops `noreturn` and `noexcept` from the function type very similarly to what this code does for effects. If this code is disabled, then an implicit conversion between the two canonically different types fails. Then `TryUserDefinedConversion` fails too. 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -4639,6 +4645,303 @@ class FunctionNoProtoType : public FunctionType, public llvm::FoldingSetNode { } }; +// -- + +/// Represents an abstract function effect, using just an enumeration describing +/// its kind. +class FunctionEffect { +public: + /// Identifies the particular effect. + enum class Kind : uint8_t { +None = 0, +NonBlocking = 1, +NonAllocating = 2, +Blocking = 3, +Allocating = 4 + }; + + /// Flags describing some behaviors of the effect. + using Flags = unsigned; + enum FlagBit : Flags { +// Can verification inspect callees' implementations? (e.g. nonblocking: +// yes, tcb+types: no). This also implies the need for 2nd-pass +// verification. +FE_InferrableOnCallees = 0x1, + +// Language constructs which effects can diagnose as disallowed. +FE_ExcludeThrow = 0x2, +FE_ExcludeCatch = 0x4, +FE_ExcludeObjCMessageSend = 0x8, +FE_ExcludeStaticLocalVars = 0x10, +FE_ExcludeThreadLocalVars = 0x20 + }; + +private: + LLVM_PREFERRED_TYPE(Kind) + unsigned FKind : 3; + + // Expansion: for hypothetical TCB+types, there could be one Kind for TCB, + // then ~16(?) bits "SubKind" to map to a specific named TCB. SubKind would + // be considered for uniqueness. + +public: + FunctionEffect() : FKind(unsigned(Kind::None)) {} + + explicit FunctionEffect(Kind K) : FKind(unsigned(K)) {} + + /// The kind of the effect. + Kind kind() const { return Kind(FKind); } + + /// Return the opposite kind, for effects which have opposites. + Kind oppositeKind() const; + + /// For serialization. + uint32_t toOpaqueInt32() const { return FKind; } + static FunctionEffect fromOpaqueInt32(uint32_t Value) { +return FunctionEffect(Kind(Value)); + } + + /// Flags describing some behaviors of the effect. + Flags flags() const { +switch (kind()) { +case Kind::NonBlocking: + return FE_InferrableOnCallees | FE_ExcludeThrow | FE_ExcludeCatch | + FE_ExcludeObjCMessageSend | FE_ExcludeStaticLocalVars | + FE_ExcludeThreadLocalVars; +case Kind::NonAllocating: + // Same as NonBlocking, except without FE_ExcludeStaticLocalVars + return FE_InferrableOnCallees | FE_ExcludeThrow | FE_ExcludeCatch | + FE_ExcludeObjCMessageSend | FE_ExcludeThreadLocalVars; +case Kind::Blocking: +case Kind::Allocating: + return 0; +case Kind::None: + break; +} +llvm_unreachable("unknown effect kind"); + } + + /// The description printed in diagnostics, e.g. 'nonblocking'. + StringRef name() const; + + /// Return true if the effect is allowed to be inferred on the callee, + /// which is either a FunctionDecl or BlockDecl. + /// This is only used if the effect has FE_InferrableOnCallees flag set. + /// Example: This allows nonblocking(false) to prevent inference for the + /// function. + bool canInferOnFunction(const Decl ) const; + + // Return false for success. When true is returned for a direct call, then the + // FE_InferrableOnCallees flag may trigger inference rather than an immediate + // diagnostic. Caller should be assumed to have the effect (it may not have it + // explicitly when inferring). + bool shouldDiagnoseFunctionCall(bool Direct, + ArrayRef CalleeFX) const; + + friend bool operator==(const FunctionEffect , const FunctionEffect ) { +return LHS.FKind == RHS.FKind; + } + friend bool operator!=(const FunctionEffect , const FunctionEffect ) { +return !(LHS == RHS); + } + friend bool operator<(const FunctionEffect , const FunctionEffect ) { +return LHS.FKind < RHS.FKind; + } +}; + +/// Wrap a function effect's condition expression in another struct so +/// that FunctionProtoType's TrailingObjects can treat it separately. +class FunctionEffectCondition { + Expr *Cond = nullptr; // if null, unconditional + +public: + FunctionEffectCondition() = default; + FunctionEffectCondition(Expr *E) : Cond(E) {} // implicit OK + + Expr *expr() const { return Cond; } + + bool operator==(const FunctionEffectCondition ) const { +return Cond == RHS.Cond; + } +}; + +/// A FunctionEffect plus a potential boolean expression determining whether +/// the effect is declared (e.g. nonblocking(expr)). Generally the condition +/// expression when present, is dependent. +struct FunctionEffectWithCondition { + FunctionEffect Effect; + FunctionEffectCondition Cond; + + FunctionEffectWithCondition() = default; + FunctionEffectWithCondition(const FunctionEffect , + const FunctionEffectCondition ) + : Effect(E), Cond(C) {} + + /// Return a textual description of the effect, and its condition, if any. + std::string description() const; + + friend bool operator<(const FunctionEffectWithCondition , +const FunctionEffectWithCondition ) { +if (LHS.Effect < RHS.Effect) +
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -4639,6 +4645,303 @@ class FunctionNoProtoType : public FunctionType, public llvm::FoldingSetNode { } }; +// -- + +/// Represents an abstract function effect, using just an enumeration describing +/// its kind. +class FunctionEffect { +public: + /// Identifies the particular effect. + enum class Kind : uint8_t { +None = 0, +NonBlocking = 1, +NonAllocating = 2, +Blocking = 3, +Allocating = 4 + }; + + /// Flags describing some behaviors of the effect. + using Flags = unsigned; + enum FlagBit : Flags { +// Can verification inspect callees' implementations? (e.g. nonblocking: +// yes, tcb+types: no). This also implies the need for 2nd-pass +// verification. +FE_InferrableOnCallees = 0x1, + +// Language constructs which effects can diagnose as disallowed. +FE_ExcludeThrow = 0x2, +FE_ExcludeCatch = 0x4, +FE_ExcludeObjCMessageSend = 0x8, +FE_ExcludeStaticLocalVars = 0x10, +FE_ExcludeThreadLocalVars = 0x20 + }; + +private: + LLVM_PREFERRED_TYPE(Kind) + unsigned FKind : 3; + + // Expansion: for hypothetical TCB+types, there could be one Kind for TCB, + // then ~16(?) bits "SubKind" to map to a specific named TCB. SubKind would + // be considered for uniqueness. + +public: + FunctionEffect() : FKind(unsigned(Kind::None)) {} + + explicit FunctionEffect(Kind K) : FKind(unsigned(K)) {} + + /// The kind of the effect. + Kind kind() const { return Kind(FKind); } + + /// Return the opposite kind, for effects which have opposites. + Kind oppositeKind() const; + + /// For serialization. + uint32_t toOpaqueInt32() const { return FKind; } + static FunctionEffect fromOpaqueInt32(uint32_t Value) { +return FunctionEffect(Kind(Value)); + } + + /// Flags describing some behaviors of the effect. + Flags flags() const { +switch (kind()) { +case Kind::NonBlocking: + return FE_InferrableOnCallees | FE_ExcludeThrow | FE_ExcludeCatch | + FE_ExcludeObjCMessageSend | FE_ExcludeStaticLocalVars | + FE_ExcludeThreadLocalVars; +case Kind::NonAllocating: + // Same as NonBlocking, except without FE_ExcludeStaticLocalVars + return FE_InferrableOnCallees | FE_ExcludeThrow | FE_ExcludeCatch | + FE_ExcludeObjCMessageSend | FE_ExcludeThreadLocalVars; +case Kind::Blocking: +case Kind::Allocating: + return 0; +case Kind::None: + break; +} +llvm_unreachable("unknown effect kind"); + } + + /// The description printed in diagnostics, e.g. 'nonblocking'. + StringRef name() const; + + /// Return true if the effect is allowed to be inferred on the callee, + /// which is either a FunctionDecl or BlockDecl. + /// This is only used if the effect has FE_InferrableOnCallees flag set. + /// Example: This allows nonblocking(false) to prevent inference for the + /// function. + bool canInferOnFunction(const Decl ) const; + + // Return false for success. When true is returned for a direct call, then the + // FE_InferrableOnCallees flag may trigger inference rather than an immediate + // diagnostic. Caller should be assumed to have the effect (it may not have it + // explicitly when inferring). + bool shouldDiagnoseFunctionCall(bool Direct, + ArrayRef CalleeFX) const; + + friend bool operator==(const FunctionEffect , const FunctionEffect ) { +return LHS.FKind == RHS.FKind; + } + friend bool operator!=(const FunctionEffect , const FunctionEffect ) { +return !(LHS == RHS); + } + friend bool operator<(const FunctionEffect , const FunctionEffect ) { +return LHS.FKind < RHS.FKind; + } +}; + +/// Wrap a function effect's condition expression in another struct so +/// that FunctionProtoType's TrailingObjects can treat it separately. +class FunctionEffectCondition { + Expr *Cond = nullptr; // if null, unconditional + +public: + FunctionEffectCondition() = default; + FunctionEffectCondition(Expr *E) : Cond(E) {} // implicit OK + + Expr *expr() const { return Cond; } + + bool operator==(const FunctionEffectCondition ) const { +return Cond == RHS.Cond; + } +}; + +/// A FunctionEffect plus a potential boolean expression determining whether +/// the effect is declared (e.g. nonblocking(expr)). Generally the condition +/// expression when present, is dependent. +struct FunctionEffectWithCondition { + FunctionEffect Effect; + FunctionEffectCondition Cond; + + FunctionEffectWithCondition() = default; + FunctionEffectWithCondition(const FunctionEffect , + const FunctionEffectCondition ) + : Effect(E), Cond(C) {} + + /// Return a textual description of the effect, and its condition, if any. + std::string description() const; + + friend bool operator<(const FunctionEffectWithCondition , +const FunctionEffectWithCondition ) { +if (LHS.Effect < RHS.Effect) +
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -5028,3 +5060,263 @@ void AutoType::Profile(llvm::FoldingSetNodeID , const ASTContext ) { Profile(ID, Context, getDeducedType(), getKeyword(), isDependentType(), getTypeConstraintConcept(), getTypeConstraintArguments()); } + +FunctionEffect::Kind FunctionEffect::oppositeKind() const { + switch (kind()) { + case Kind::NonBlocking: +return Kind::Blocking; + case Kind::Blocking: +return Kind::NonBlocking; + case Kind::NonAllocating: +return Kind::Allocating; + case Kind::Allocating: +return Kind::NonAllocating; + case Kind::None: +return Kind::None; + } + llvm_unreachable("unknown effect kind"); +} + +StringRef FunctionEffect::name() const { + switch (kind()) { + case Kind::NonBlocking: +return "nonblocking"; + case Kind::NonAllocating: +return "nonallocating"; + case Kind::Blocking: +return "blocking"; + case Kind::Allocating: +return "allocating"; + case Kind::None: +break; + } + llvm_unreachable("unknown effect kind"); +} + +bool FunctionEffect::canInferOnFunction(const Decl ) const { + switch (kind()) { + case Kind::NonAllocating: + case Kind::NonBlocking: { +FunctionEffectsRef CalleeFX; +if (auto *FD = Callee.getAsFunction()) + CalleeFX = FD->getFunctionEffects(); +else if (auto *BD = dyn_cast()) + CalleeFX = BD->getFunctionEffects(); +else + return false; +for (const FunctionEffectWithCondition : CalleeFX) { + // nonblocking/nonallocating cannot call allocating + if (CalleeEC.Effect.kind() == Kind::Allocating) +return false; + // nonblocking cannot call blocking + if (kind() == Kind::NonBlocking && + CalleeEC.Effect.kind() == Kind::Blocking) +return false; +} + } +return true; + + case Kind::Allocating: + case Kind::Blocking: +return false; + + case Kind::None: +break; + } + llvm_unreachable("unknown effect kind"); +} + +bool FunctionEffect::shouldDiagnoseFunctionCall( +bool Direct, ArrayRef CalleeFX) const { + switch (kind()) { + case Kind::NonAllocating: + case Kind::NonBlocking: { +const Kind CallerKind = kind(); +for (const auto : CalleeFX) { + const Kind EK = Effect.kind(); + // Does callee have same or stronger constraint? + if (EK == CallerKind || + (CallerKind == Kind::NonAllocating && EK == Kind::NonBlocking)) { +return false; // no diagnostic + } +} +return true; // warning + } + case Kind::Allocating: + case Kind::Blocking: +return false; + case Kind::None: +break; dougsonos wrote: done 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -5028,3 +5060,263 @@ void AutoType::Profile(llvm::FoldingSetNodeID , const ASTContext ) { Profile(ID, Context, getDeducedType(), getKeyword(), isDependentType(), getTypeConstraintConcept(), getTypeConstraintArguments()); } + +FunctionEffect::Kind FunctionEffect::oppositeKind() const { + switch (kind()) { + case Kind::NonBlocking: +return Kind::Blocking; + case Kind::Blocking: +return Kind::NonBlocking; + case Kind::NonAllocating: +return Kind::Allocating; + case Kind::Allocating: +return Kind::NonAllocating; + case Kind::None: +return Kind::None; + } + llvm_unreachable("unknown effect kind"); +} + +StringRef FunctionEffect::name() const { + switch (kind()) { + case Kind::NonBlocking: +return "nonblocking"; + case Kind::NonAllocating: +return "nonallocating"; + case Kind::Blocking: +return "blocking"; + case Kind::Allocating: +return "allocating"; + case Kind::None: +break; + } + llvm_unreachable("unknown effect kind"); +} + +bool FunctionEffect::canInferOnFunction(const Decl ) const { + switch (kind()) { + case Kind::NonAllocating: + case Kind::NonBlocking: { +FunctionEffectsRef CalleeFX; +if (auto *FD = Callee.getAsFunction()) + CalleeFX = FD->getFunctionEffects(); +else if (auto *BD = dyn_cast()) + CalleeFX = BD->getFunctionEffects(); +else + return false; +for (const FunctionEffectWithCondition : CalleeFX) { + // nonblocking/nonallocating cannot call allocating + if (CalleeEC.Effect.kind() == Kind::Allocating) +return false; + // nonblocking cannot call blocking + if (kind() == Kind::NonBlocking && + CalleeEC.Effect.kind() == Kind::Blocking) +return false; +} + } +return true; + + case Kind::Allocating: + case Kind::Blocking: +return false; + + case Kind::None: +break; dougsonos wrote: done 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -5028,3 +5060,263 @@ void AutoType::Profile(llvm::FoldingSetNodeID , const ASTContext ) { Profile(ID, Context, getDeducedType(), getKeyword(), isDependentType(), getTypeConstraintConcept(), getTypeConstraintArguments()); } + +FunctionEffect::Kind FunctionEffect::oppositeKind() const { + switch (kind()) { + case Kind::NonBlocking: +return Kind::Blocking; + case Kind::Blocking: +return Kind::NonBlocking; + case Kind::NonAllocating: +return Kind::Allocating; + case Kind::Allocating: +return Kind::NonAllocating; + case Kind::None: +return Kind::None; + } + llvm_unreachable("unknown effect kind"); +} + +StringRef FunctionEffect::name() const { + switch (kind()) { + case Kind::NonBlocking: +return "nonblocking"; + case Kind::NonAllocating: +return "nonallocating"; + case Kind::Blocking: +return "blocking"; + case Kind::Allocating: +return "allocating"; + case Kind::None: +break; dougsonos wrote: done 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -1435,6 +1435,38 @@ def CXX11NoReturn : InheritableAttr { let Documentation = [CXX11NoReturnDocs]; } +def NonBlocking : TypeAttr { + let Spellings = [CXX11<"clang", "nonblocking">, dougsonos wrote: I've gotten rid of the "clang_" prefix on the GNU-style attributes. 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -4639,6 +4645,303 @@ class FunctionNoProtoType : public FunctionType, public llvm::FoldingSetNode { } }; +// -- + +/// Represents an abstract function effect, using just an enumeration describing +/// its kind. +class FunctionEffect { +public: + /// Identifies the particular effect. + enum class Kind : uint8_t { +None = 0, +NonBlocking = 1, +NonAllocating = 2, +Blocking = 3, +Allocating = 4 + }; + + /// Flags describing some behaviors of the effect. + using Flags = unsigned; + enum FlagBit : Flags { +// Can verification inspect callees' implementations? (e.g. nonblocking: +// yes, tcb+types: no). This also implies the need for 2nd-pass +// verification. +FE_InferrableOnCallees = 0x1, + +// Language constructs which effects can diagnose as disallowed. +FE_ExcludeThrow = 0x2, +FE_ExcludeCatch = 0x4, +FE_ExcludeObjCMessageSend = 0x8, +FE_ExcludeStaticLocalVars = 0x10, +FE_ExcludeThreadLocalVars = 0x20 + }; + +private: + LLVM_PREFERRED_TYPE(Kind) + unsigned FKind : 3; + + // Expansion: for hypothetical TCB+types, there could be one Kind for TCB, + // then ~16(?) bits "SubKind" to map to a specific named TCB. SubKind would + // be considered for uniqueness. + +public: + FunctionEffect() : FKind(unsigned(Kind::None)) {} + + explicit FunctionEffect(Kind K) : FKind(unsigned(K)) {} + + /// The kind of the effect. + Kind kind() const { return Kind(FKind); } + + /// Return the opposite kind, for effects which have opposites. + Kind oppositeKind() const; + + /// For serialization. + uint32_t toOpaqueInt32() const { return FKind; } + static FunctionEffect fromOpaqueInt32(uint32_t Value) { +return FunctionEffect(Kind(Value)); + } + + /// Flags describing some behaviors of the effect. + Flags flags() const { +switch (kind()) { +case Kind::NonBlocking: + return FE_InferrableOnCallees | FE_ExcludeThrow | FE_ExcludeCatch | + FE_ExcludeObjCMessageSend | FE_ExcludeStaticLocalVars | + FE_ExcludeThreadLocalVars; +case Kind::NonAllocating: + // Same as NonBlocking, except without FE_ExcludeStaticLocalVars + return FE_InferrableOnCallees | FE_ExcludeThrow | FE_ExcludeCatch | + FE_ExcludeObjCMessageSend | FE_ExcludeThreadLocalVars; +case Kind::Blocking: +case Kind::Allocating: + return 0; +case Kind::None: + break; +} +llvm_unreachable("unknown effect kind"); + } + + /// The description printed in diagnostics, e.g. 'nonblocking'. + StringRef name() const; + + /// Return true if the effect is allowed to be inferred on the callee, + /// which is either a FunctionDecl or BlockDecl. + /// This is only used if the effect has FE_InferrableOnCallees flag set. + /// Example: This allows nonblocking(false) to prevent inference for the + /// function. + bool canInferOnFunction(const Decl ) const; + + // Return false for success. When true is returned for a direct call, then the + // FE_InferrableOnCallees flag may trigger inference rather than an immediate + // diagnostic. Caller should be assumed to have the effect (it may not have it + // explicitly when inferring). + bool shouldDiagnoseFunctionCall(bool Direct, + ArrayRef CalleeFX) const; + + friend bool operator==(const FunctionEffect , const FunctionEffect ) { +return LHS.FKind == RHS.FKind; + } + friend bool operator!=(const FunctionEffect , const FunctionEffect ) { +return !(LHS == RHS); + } + friend bool operator<(const FunctionEffect , const FunctionEffect ) { +return LHS.FKind < RHS.FKind; + } +}; + +/// Wrap a function effect's condition expression in another struct so +/// that FunctionProtoType's TrailingObjects can treat it separately. +class FunctionEffectCondition { + Expr *Cond = nullptr; // if null, unconditional + +public: + FunctionEffectCondition() = default; + FunctionEffectCondition(Expr *E) : Cond(E) {} // implicit OK + + Expr *expr() const { return Cond; } + + bool operator==(const FunctionEffectCondition ) const { +return Cond == RHS.Cond; + } +}; + +/// A FunctionEffect plus a potential boolean expression determining whether +/// the effect is declared (e.g. nonblocking(expr)). Generally the condition +/// expression when present, is dependent. +struct FunctionEffectWithCondition { + FunctionEffect Effect; + FunctionEffectCondition Cond; + + FunctionEffectWithCondition() = default; + FunctionEffectWithCondition(const FunctionEffect , + const FunctionEffectCondition ) + : Effect(E), Cond(C) {} + + /// Return a textual description of the effect, and its condition, if any. + std::string description() const; + + friend bool operator<(const FunctionEffectWithCondition , +const FunctionEffectWithCondition ) { +if (LHS.Effect < RHS.Effect) +
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -4639,6 +4645,303 @@ class FunctionNoProtoType : public FunctionType, public llvm::FoldingSetNode { } }; +// -- + +/// Represents an abstract function effect, using just an enumeration describing +/// its kind. +class FunctionEffect { +public: + /// Identifies the particular effect. + enum class Kind : uint8_t { +None = 0, +NonBlocking = 1, +NonAllocating = 2, +Blocking = 3, +Allocating = 4 + }; + + /// Flags describing some behaviors of the effect. + using Flags = unsigned; + enum FlagBit : Flags { +// Can verification inspect callees' implementations? (e.g. nonblocking: +// yes, tcb+types: no). This also implies the need for 2nd-pass +// verification. +FE_InferrableOnCallees = 0x1, + +// Language constructs which effects can diagnose as disallowed. +FE_ExcludeThrow = 0x2, +FE_ExcludeCatch = 0x4, +FE_ExcludeObjCMessageSend = 0x8, +FE_ExcludeStaticLocalVars = 0x10, +FE_ExcludeThreadLocalVars = 0x20 + }; + +private: + LLVM_PREFERRED_TYPE(Kind) + unsigned FKind : 3; + + // Expansion: for hypothetical TCB+types, there could be one Kind for TCB, + // then ~16(?) bits "SubKind" to map to a specific named TCB. SubKind would + // be considered for uniqueness. + +public: + FunctionEffect() : FKind(unsigned(Kind::None)) {} + + explicit FunctionEffect(Kind K) : FKind(unsigned(K)) {} + + /// The kind of the effect. + Kind kind() const { return Kind(FKind); } + + /// Return the opposite kind, for effects which have opposites. + Kind oppositeKind() const; + + /// For serialization. + uint32_t toOpaqueInt32() const { return FKind; } + static FunctionEffect fromOpaqueInt32(uint32_t Value) { +return FunctionEffect(Kind(Value)); + } + + /// Flags describing some behaviors of the effect. + Flags flags() const { +switch (kind()) { +case Kind::NonBlocking: + return FE_InferrableOnCallees | FE_ExcludeThrow | FE_ExcludeCatch | + FE_ExcludeObjCMessageSend | FE_ExcludeStaticLocalVars | + FE_ExcludeThreadLocalVars; +case Kind::NonAllocating: + // Same as NonBlocking, except without FE_ExcludeStaticLocalVars + return FE_InferrableOnCallees | FE_ExcludeThrow | FE_ExcludeCatch | + FE_ExcludeObjCMessageSend | FE_ExcludeThreadLocalVars; +case Kind::Blocking: +case Kind::Allocating: + return 0; +case Kind::None: + break; +} +llvm_unreachable("unknown effect kind"); + } + + /// The description printed in diagnostics, e.g. 'nonblocking'. + StringRef name() const; + + /// Return true if the effect is allowed to be inferred on the callee, + /// which is either a FunctionDecl or BlockDecl. + /// This is only used if the effect has FE_InferrableOnCallees flag set. + /// Example: This allows nonblocking(false) to prevent inference for the + /// function. + bool canInferOnFunction(const Decl ) const; + + // Return false for success. When true is returned for a direct call, then the + // FE_InferrableOnCallees flag may trigger inference rather than an immediate + // diagnostic. Caller should be assumed to have the effect (it may not have it + // explicitly when inferring). + bool shouldDiagnoseFunctionCall(bool Direct, + ArrayRef CalleeFX) const; + + friend bool operator==(const FunctionEffect , const FunctionEffect ) { +return LHS.FKind == RHS.FKind; + } + friend bool operator!=(const FunctionEffect , const FunctionEffect ) { +return !(LHS == RHS); + } + friend bool operator<(const FunctionEffect , const FunctionEffect ) { +return LHS.FKind < RHS.FKind; + } +}; + +/// Wrap a function effect's condition expression in another struct so +/// that FunctionProtoType's TrailingObjects can treat it separately. +class FunctionEffectCondition { + Expr *Cond = nullptr; // if null, unconditional + +public: + FunctionEffectCondition() = default; + FunctionEffectCondition(Expr *E) : Cond(E) {} // implicit OK + + Expr *expr() const { return Cond; } dougsonos wrote: done 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -395,6 +395,33 @@ bool Sema::checkStringLiteralArgumentAttr(const ParsedAttr , unsigned ArgNum, return checkStringLiteralArgumentAttr(AL, ArgExpr, Str, ArgLocation); } +/// Check if the argument \p ArgNum of \p Attr is a compile-time constant +/// integer (boolean) expression. If not, emit an error and return false. +bool Sema::checkBoolExprArgumentAttr(const ParsedAttr , unsigned ArgNum, + bool ) { + if (AL.isInvalid()) { +return false; + } + Expr *ArgExpr = AL.getArgAsExpr(ArgNum); + SourceLocation ErrorLoc(AL.getLoc()); + + if (AL.isArgIdent(ArgNum)) { +IdentifierLoc *IL = AL.getArgAsIdent(ArgNum); +ErrorLoc = IL->Loc; + } else if (ArgExpr != nullptr) { +if (const std::optional MaybeVal = +ArgExpr->getIntegerConstantExpr(Context, )) { + Value = MaybeVal->getBoolValue(); + return true; +} + } dougsonos wrote: I think this is resolved now...? 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -661,6 +681,11 @@ ExprResult Sema::ImpCastExprToType(Expr *E, QualType Ty, diagnoseNullableToNonnullConversion(Ty, E->getType(), E->getBeginLoc()); diagnoseZeroToNullptrConversion(Kind, E); + if (!isCast(CCK) && !E->isNullPointerConstant( + Context, Expr::NPC_NeverValueDependent /* ???*/)) { dougsonos wrote: I found a much better way to check for a null pointer; there's already another local enum that says. 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -4639,6 +4645,303 @@ class FunctionNoProtoType : public FunctionType, public llvm::FoldingSetNode { } }; +// -- + +/// Represents an abstract function effect, using just an enumeration describing +/// its kind. +class FunctionEffect { +public: + /// Identifies the particular effect. + enum class Kind : uint8_t { +None = 0, +NonBlocking = 1, +NonAllocating = 2, +Blocking = 3, +Allocating = 4 + }; + + /// Flags describing some behaviors of the effect. + using Flags = unsigned; + enum FlagBit : Flags { +// Can verification inspect callees' implementations? (e.g. nonblocking: +// yes, tcb+types: no). This also implies the need for 2nd-pass +// verification. +FE_InferrableOnCallees = 0x1, + +// Language constructs which effects can diagnose as disallowed. +FE_ExcludeThrow = 0x2, +FE_ExcludeCatch = 0x4, +FE_ExcludeObjCMessageSend = 0x8, +FE_ExcludeStaticLocalVars = 0x10, +FE_ExcludeThreadLocalVars = 0x20 + }; + +private: + LLVM_PREFERRED_TYPE(Kind) + unsigned FKind : 3; + + // Expansion: for hypothetical TCB+types, there could be one Kind for TCB, + // then ~16(?) bits "SubKind" to map to a specific named TCB. SubKind would + // be considered for uniqueness. + +public: + FunctionEffect() : FKind(unsigned(Kind::None)) {} + + explicit FunctionEffect(Kind K) : FKind(unsigned(K)) {} + + /// The kind of the effect. + Kind kind() const { return Kind(FKind); } + + /// Return the opposite kind, for effects which have opposites. + Kind oppositeKind() const; + + /// For serialization. + uint32_t toOpaqueInt32() const { return FKind; } + static FunctionEffect fromOpaqueInt32(uint32_t Value) { +return FunctionEffect(Kind(Value)); + } + + /// Flags describing some behaviors of the effect. + Flags flags() const { +switch (kind()) { +case Kind::NonBlocking: + return FE_InferrableOnCallees | FE_ExcludeThrow | FE_ExcludeCatch | + FE_ExcludeObjCMessageSend | FE_ExcludeStaticLocalVars | + FE_ExcludeThreadLocalVars; +case Kind::NonAllocating: + // Same as NonBlocking, except without FE_ExcludeStaticLocalVars + return FE_InferrableOnCallees | FE_ExcludeThrow | FE_ExcludeCatch | + FE_ExcludeObjCMessageSend | FE_ExcludeThreadLocalVars; +case Kind::Blocking: +case Kind::Allocating: + return 0; +case Kind::None: + break; +} +llvm_unreachable("unknown effect kind"); + } + + /// The description printed in diagnostics, e.g. 'nonblocking'. + StringRef name() const; + + /// Return true if the effect is allowed to be inferred on the callee, + /// which is either a FunctionDecl or BlockDecl. + /// This is only used if the effect has FE_InferrableOnCallees flag set. + /// Example: This allows nonblocking(false) to prevent inference for the + /// function. + bool canInferOnFunction(const Decl ) const; + + // Return false for success. When true is returned for a direct call, then the + // FE_InferrableOnCallees flag may trigger inference rather than an immediate + // diagnostic. Caller should be assumed to have the effect (it may not have it + // explicitly when inferring). + bool shouldDiagnoseFunctionCall(bool Direct, + ArrayRef CalleeFX) const; + + friend bool operator==(const FunctionEffect , const FunctionEffect ) { +return LHS.FKind == RHS.FKind; + } + friend bool operator!=(const FunctionEffect , const FunctionEffect ) { +return !(LHS == RHS); + } + friend bool operator<(const FunctionEffect , const FunctionEffect ) { +return LHS.FKind < RHS.FKind; + } +}; + +/// Wrap a function effect's condition expression in another struct so +/// that FunctionProtoType's TrailingObjects can treat it separately. +class FunctionEffectCondition { + Expr *Cond = nullptr; // if null, unconditional + +public: + FunctionEffectCondition() = default; + FunctionEffectCondition(Expr *E) : Cond(E) {} // implicit OK + + Expr *expr() const { return Cond; } + + bool operator==(const FunctionEffectCondition ) const { +return Cond == RHS.Cond; + } +}; + +/// A FunctionEffect plus a potential boolean expression determining whether +/// the effect is declared (e.g. nonblocking(expr)). Generally the condition +/// expression when present, is dependent. +struct FunctionEffectWithCondition { + FunctionEffect Effect; + FunctionEffectCondition Cond; + + FunctionEffectWithCondition() = default; + FunctionEffectWithCondition(const FunctionEffect , + const FunctionEffectCondition ) + : Effect(E), Cond(C) {} + + /// Return a textual description of the effect, and its condition, if any. + std::string description() const; + + friend bool operator<(const FunctionEffectWithCondition , +const FunctionEffectWithCondition ) { +if (LHS.Effect < RHS.Effect) +
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -7963,6 +7968,154 @@ static Attr *getCCTypeAttr(ASTContext , ParsedAttr ) { llvm_unreachable("unexpected attribute kind!"); } +std::optional +Sema::ActOnEffectExpression(Expr *CondExpr, StringRef AttributeName) { + auto BadExpr = [&]() { +Diag(CondExpr->getExprLoc(), diag::err_attribute_argument_type) +<< ("'" + AttributeName.str() + "'") << AANT_ArgumentIntegerConstant +<< CondExpr->getSourceRange(); +return std::nullopt; + }; + + if (CondExpr->isTypeDependent() || CondExpr->isValueDependent()) { +if (CondExpr->containsUnexpandedParameterPack()) + return BadExpr(); +return FunctionEffectMode::Dependent; + } + + std::optional ConditionValue = + CondExpr->getIntegerConstantExpr(Context); + if (!ConditionValue) +return BadExpr(); + return ConditionValue->getExtValue() ? FunctionEffectMode::True + : FunctionEffectMode::False; +} + +static bool +handleNonBlockingNonAllocatingTypeAttr(TypeProcessingState , + ParsedAttr , QualType , + FunctionTypeUnwrapper ) { + // Delay if this is not a function type. + if (!Unwrapped.isFunctionType()) +return false; + + Sema = TPState.getSema(); + + // Require FunctionProtoType + auto *FPT = Unwrapped.get()->getAs(); + if (FPT == nullptr) { +S.Diag(PAttr.getLoc(), diag::err_func_with_effects_no_prototype) +<< PAttr.getAttrName()->getName(); +return true; + } + + // Parse the new attribute. + // non/blocking or non/allocating? Or conditional (computed)? + const bool IsNonBlocking = PAttr.getKind() == ParsedAttr::AT_NonBlocking || dougsonos wrote: Not sure I understand the motivation to avoid `const` local variables - to me it reduces complexity for the reader. 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -1435,6 +1435,38 @@ def CXX11NoReturn : InheritableAttr { let Documentation = [CXX11NoReturnDocs]; } +def NonBlocking : TypeAttr { + let Spellings = [CXX11<"clang", "nonblocking">, dougsonos wrote: I don't really care either way, but there was a comment suggesting namespacing the GNU-style attribute, just in case GNU were to add their own attribute with the same name. There is also precedent: `clang_builtin_alias` 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -0,0 +1,126 @@ +// RUN: %clang_cc1 -fsyntax-only -fblocks -fcxx-exceptions -verify %s +// RUN: %clang_cc1 -fsyntax-only -fblocks -verify -x c -std=c23 %s dougsonos wrote: There's now a separate file for pre-C23 - currently it just tests the prototypeless case. I'm having trouble thinking of more things to test for pre-C23 that are different from what's covered in this file for C23. 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -1870,6 +1870,28 @@ bool Sema::IsFunctionConversion(QualType FromType, QualType ToType, FromFn = QT->getAs(); Changed = true; } + +// For C, when called from checkPointerTypesForAssignment, +// we need not to alter FromFn, or else even an innocuous cast +// like dropping effects will fail. In C++ however we do want to +// alter FromFn. TODO: Is this correct? dougsonos wrote: I think C ends up doing something similar but in a different place, `ASTContext::mergeFunctionTypes()` 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -10778,6 +10778,101 @@ def warn_imp_cast_drops_unaligned : Warning< "implicit cast from type %0 to type %1 drops __unaligned qualifier">, InGroup>; +def warn_func_effect_allocates : Warning< + "'%0' function '%1' must not allocate or deallocate memory">, + InGroup; + +def note_func_effect_allocates : Note< + "'%1' cannot be inferred '%0' because it allocates/deallocates memory">; + +def warn_func_effect_throws_or_catches : Warning< + "'%0' function '%1' must not throw or catch exceptions">, + InGroup; + +def note_func_effect_throws_or_catches : Note< + "'%1' cannot be inferred '%0' because it throws or catches exceptions">; + +def warn_func_effect_has_static_local : Warning< + "'%0' function '%1' must not have static locals">, + InGroup; + +def note_func_effect_has_static_local : Note< + "'%1' cannot be inferred '%0' because it has a static local">; + +def warn_func_effect_uses_thread_local : Warning< + "'%0' function '%1' must not use thread-local variables">, + InGroup; + +def note_func_effect_uses_thread_local : Note< + "'%1' cannot be inferred '%0' because it uses a thread-local variable">; + +def warn_func_effect_calls_objc : Warning< + "'%0' function '%1' must not access an ObjC method or property">, + InGroup; + +def note_func_effect_calls_objc : Note< + "'%1' cannot be inferred '%0' because it accesses an ObjC method or property">; + +def warn_func_effect_calls_disallowed_func : Warning< + "'%0' function '%1' must not call non-'%0' function '%2'">, + InGroup; + +// UNTESTED +def warn_func_effect_calls_disallowed_expr : Warning< + "'%0' function '%1' must not call non-'%0' expression">, + InGroup; + +def note_func_effect_calls_disallowed_func : Note< + "'%1' cannot be inferred '%0' because it calls non-'%0' function '%2'">; + +def note_func_effect_call_extern : Note< + "'%1' cannot be inferred '%0' because it has no definition in this translation unit">; + +def note_func_effect_call_not_inferrable : Note< + "'%1' does not permit inference of '%0'">; + +def note_func_effect_call_virtual : Note< + "'%1' cannot be inferred '%0' because it is virtual">; + +def note_func_effect_call_func_ptr : Note< + "'%1' cannot be inferred '%0' because it is a function pointer">; + +// TODO: Not currently being generated +// def warn_perf_annotation_implies_noexcept : Warning< +// "'%0' function should be declared noexcept">, +// InGroup; + +// TODO: Not currently being generated +def warn_func_effect_false_on_type : Warning< + "only functions/methods/blocks may be declared %0(false)">, + InGroup; + +// TODO: can the usual template expansion notes be used? +def note_func_effect_from_template : Note< + "in template expansion here">; dougsonos wrote: This isn't part of this branch any more; we'll need to revisit on the second PR. 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -10778,6 +10778,101 @@ def warn_imp_cast_drops_unaligned : Warning< "implicit cast from type %0 to type %1 drops __unaligned qualifier">, InGroup>; +def warn_func_effect_allocates : Warning< + "'%0' function '%1' must not allocate or deallocate memory">, + InGroup; + +def note_func_effect_allocates : Note< + "'%1' cannot be inferred '%0' because it allocates/deallocates memory">; dougsonos wrote: These diagnostics aren't part of this branch any more. 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -7963,6 +7967,148 @@ static Attr *getCCTypeAttr(ASTContext , ParsedAttr ) { llvm_unreachable("unexpected attribute kind!"); } +ExprResult Sema::ActOnEffectExpression(Expr *CondExpr, FunctionEffectMode , + bool RequireConstexpr) { + // see checkFunctionConditionAttr, Sema::CheckCXXBooleanCondition + if (RequireConstexpr || !CondExpr->isTypeDependent()) { +ExprResult E = PerformContextuallyConvertToBool(CondExpr); +if (E.isInvalid()) + return E; +CondExpr = E.get(); +if (RequireConstexpr || !CondExpr->isValueDependent()) { + llvm::APSInt CondInt; + E = VerifyIntegerConstantExpression( dougsonos wrote: Thanks. With the unexpanded pack: During initial parsing of the function type, it's seen as dependent and represented that way, holding onto the condition. Is there a way at this time to detect that the expression is not only dependent but an unexpanded pack? Maybe it's as simple as `E->containsUnexpandedParameterPack()`? That looks promising, but it's bugging me that there are no (obvious) precedents for this. If I add `void g() { f(); }` there is a second attempt to resolve the expression but it is still dependent, so clearly there is an error here. But I can imagine cases where it takes more than one level of template instantiation for a dependent expression to be resolved. 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -7963,6 +7967,148 @@ static Attr *getCCTypeAttr(ASTContext , ParsedAttr ) { llvm_unreachable("unexpected attribute kind!"); } +ExprResult Sema::ActOnEffectExpression(Expr *CondExpr, FunctionEffectMode , + bool RequireConstexpr) { + // see checkFunctionConditionAttr, Sema::CheckCXXBooleanCondition + if (RequireConstexpr || !CondExpr->isTypeDependent()) { +ExprResult E = PerformContextuallyConvertToBool(CondExpr); +if (E.isInvalid()) + return E; +CondExpr = E.get(); +if (RequireConstexpr || !CondExpr->isValueDependent()) { + llvm::APSInt CondInt; + E = VerifyIntegerConstantExpression( dougsonos wrote: > Also, can you add some test cases with placeholder expressions as the > argument, e.g. `[[clang::nonblocking(__builtin_memset)]]`, and also a case > where the argument is an unexpanded pack? Not quite sure how to construct a test that won't fail in uninteresting other ways ... e.g. ``` void t5() [[clang::nonblocking(__builtin_memset)]] {} // error: nonblocking attribute requires an integer constant // (via err_attribute_argument_type) template struct ValueDependent {}; template struct TypeDependent {}; ValueDependent<__builtin_memset> x3; // error: builtin functions must be directly called TypeDependent<__builtin_memset> x3; // error: template argument for template type parameter must be a type ``` I wasn't able to find examples of tests using unexpanded packs, at least not in Sema. Would appreciate any tips, thanks. I've got a few more basic tests done and will push those shortly. 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
[clang] [llvm] [clang backend] In AArch64's DataLayout, specify a minimum function alignment of 4. (PR #90702)
dougsonos wrote: > Which email address do you want the commit associated with? The GitHub > "squash and merge" button wants to attribute it to your "sonosphere.com" > address, but it looks like the commits themselves are using an "apple.com" > address. Well neither will last forever, but hopefully sonosphere.com lasts a bit longer, so use that one please :) Thanks https://github.com/llvm/llvm-project/pull/90702 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
https://github.com/dougsonos edited 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -4639,6 +4644,312 @@ class FunctionNoProtoType : public FunctionType, public llvm::FoldingSetNode { } }; +// -- + +class Decl; +class CXXMethodDecl; +struct FunctionEffectDiff; +class FunctionEffectsRef; +class FunctionEffectSet; dougsonos wrote: Cleaned up 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -4639,6 +4644,312 @@ class FunctionNoProtoType : public FunctionType, public llvm::FoldingSetNode { } }; +// -- + +class Decl; +class CXXMethodDecl; +struct FunctionEffectDiff; +class FunctionEffectsRef; +class FunctionEffectSet; + +/// Represents an abstract function effect, using just an enumeration describing +/// its kind. +class FunctionEffect { +public: + /// Identifies the particular effect. + enum class Kind : uint8_t { +None = 0, +NonBlocking = 1, +NonAllocating = 2, +Blocking = 3, +Allocating = 4 + }; + + /// Flags describing some behaviors of the effect. + using Flags = unsigned; + enum FlagBit : Flags { +// Can verification inspect callees' implementations? (e.g. nonblocking: +// yes, tcb+types: no). This also implies the need for 2nd-pass +// verification. +FE_InferrableOnCallees = 0x1, + +// Language constructs which effects can diagnose as disallowed. +FE_ExcludeThrow = 0x2, +FE_ExcludeCatch = 0x4, +FE_ExcludeObjCMessageSend = 0x8, +FE_ExcludeStaticLocalVars = 0x10, +FE_ExcludeThreadLocalVars = 0x20 + }; + +private: + LLVM_PREFERRED_TYPE(Kind) + unsigned FKind : 3; + + // Expansion: for hypothetical TCB+types, there could be one Kind for TCB, + // then ~16(?) bits "SubKind" to map to a specific named TCB. SubKind would + // be considered for uniqueness. + +public: + FunctionEffect() : FKind(unsigned(Kind::None)) {} + + explicit FunctionEffect(Kind K) : FKind(unsigned(K)) {} + + /// The kind of the effect. + Kind kind() const { return Kind(FKind); } + + /// Return the opposite kind, for effects which have opposites. + Kind oppositeKind() const; + + /// For serialization. + uint32_t toOpaqueInt32() const { return FKind; } + static FunctionEffect fromOpaqueInt32(uint32_t Value) { +return FunctionEffect(Kind(Value)); + } + + /// Flags describing some behaviors of the effect. + Flags flags() const { +switch (kind()) { +case Kind::NonBlocking: + return FE_InferrableOnCallees | FE_ExcludeThrow | FE_ExcludeCatch | + FE_ExcludeObjCMessageSend | FE_ExcludeStaticLocalVars | + FE_ExcludeThreadLocalVars; +case Kind::NonAllocating: + // Same as NonBlocking, except without FE_ExcludeStaticLocalVars + return FE_InferrableOnCallees | FE_ExcludeThrow | FE_ExcludeCatch | + FE_ExcludeObjCMessageSend | FE_ExcludeThreadLocalVars; +case Kind::Blocking: +case Kind::Allocating: + return 0; +case Kind::None: + break; +} +llvm_unreachable("unknown effect kind"); + } + + /// The description printed in diagnostics, e.g. 'nonblocking'. + StringRef name() const; + + /// Return true if the effect is allowed to be inferred on the callee, + /// which is either a FunctionDecl or BlockDecl. + /// This is only used if the effect has FE_InferrableOnCallees flag set. + /// Example: This allows nonblocking(false) to prevent inference for the + /// function. + bool canInferOnFunction(const Decl ) const; + + // Return false for success. When true is returned for a direct call, then the + // FE_InferrableOnCallees flag may trigger inference rather than an immediate + // diagnostic. Caller should be assumed to have the effect (it may not have it + // explicitly when inferring). + bool shouldDiagnoseFunctionCall(bool Direct, + ArrayRef CalleeFX) const; + + friend bool operator==(const FunctionEffect , const FunctionEffect ) { +return LHS.FKind == RHS.FKind; + } + friend bool operator!=(const FunctionEffect , const FunctionEffect ) { +return !(LHS == RHS); + } + friend bool operator<(const FunctionEffect , const FunctionEffect ) { +return LHS.FKind < RHS.FKind; + } +}; + +/// Wrap a function effect's condition expression in another struct so +/// that FunctionProtoType's TrailingObjects can treat it separately. +class FunctionEffectCondition { + Expr *Cond = nullptr; // if null, unconditional + +public: + FunctionEffectCondition() = default; + FunctionEffectCondition(Expr *E) : Cond(E) {} // implicit OK + + Expr *expr() const { return Cond; } + + bool operator==(const FunctionEffectCondition ) const { +return Cond == RHS.Cond; + } +}; + +/// A FunctionEffect plus a potential boolean expression determining whether +/// the effect is declared (e.g. nonblocking(expr)). Generally the condition +/// expression when present, is dependent. +struct FunctionEffectWithCondition { + FunctionEffect Effect; + FunctionEffectCondition Cond; + + /// Return a textual description of the effect, and its condition, if any. + std::string description() const; +}; + +struct FunctionEffectDiff { dougsonos wrote: Moved to Sema https://github.com/llvm/llvm-project/pull/84983 ___ cfe-commits mailing list cfe-commits@lists.llvm.org
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -4639,6 +4644,312 @@ class FunctionNoProtoType : public FunctionType, public llvm::FoldingSetNode { } }; +// -- + +class Decl; +class CXXMethodDecl; +struct FunctionEffectDiff; +class FunctionEffectsRef; +class FunctionEffectSet; + +/// Represents an abstract function effect, using just an enumeration describing +/// its kind. +class FunctionEffect { +public: + /// Identifies the particular effect. + enum class Kind : uint8_t { +None = 0, +NonBlocking = 1, +NonAllocating = 2, +Blocking = 3, +Allocating = 4 + }; + + /// Flags describing some behaviors of the effect. + using Flags = unsigned; + enum FlagBit : Flags { +// Can verification inspect callees' implementations? (e.g. nonblocking: +// yes, tcb+types: no). This also implies the need for 2nd-pass +// verification. +FE_InferrableOnCallees = 0x1, + +// Language constructs which effects can diagnose as disallowed. +FE_ExcludeThrow = 0x2, +FE_ExcludeCatch = 0x4, +FE_ExcludeObjCMessageSend = 0x8, +FE_ExcludeStaticLocalVars = 0x10, +FE_ExcludeThreadLocalVars = 0x20 + }; + +private: + LLVM_PREFERRED_TYPE(Kind) + unsigned FKind : 3; + + // Expansion: for hypothetical TCB+types, there could be one Kind for TCB, + // then ~16(?) bits "SubKind" to map to a specific named TCB. SubKind would + // be considered for uniqueness. + +public: + FunctionEffect() : FKind(unsigned(Kind::None)) {} + + explicit FunctionEffect(Kind K) : FKind(unsigned(K)) {} + + /// The kind of the effect. + Kind kind() const { return Kind(FKind); } + + /// Return the opposite kind, for effects which have opposites. + Kind oppositeKind() const; + + /// For serialization. + uint32_t toOpaqueInt32() const { return FKind; } + static FunctionEffect fromOpaqueInt32(uint32_t Value) { +return FunctionEffect(Kind(Value)); + } + + /// Flags describing some behaviors of the effect. + Flags flags() const { +switch (kind()) { +case Kind::NonBlocking: + return FE_InferrableOnCallees | FE_ExcludeThrow | FE_ExcludeCatch | + FE_ExcludeObjCMessageSend | FE_ExcludeStaticLocalVars | + FE_ExcludeThreadLocalVars; +case Kind::NonAllocating: + // Same as NonBlocking, except without FE_ExcludeStaticLocalVars + return FE_InferrableOnCallees | FE_ExcludeThrow | FE_ExcludeCatch | + FE_ExcludeObjCMessageSend | FE_ExcludeThreadLocalVars; +case Kind::Blocking: +case Kind::Allocating: + return 0; +case Kind::None: + break; +} +llvm_unreachable("unknown effect kind"); + } + + /// The description printed in diagnostics, e.g. 'nonblocking'. + StringRef name() const; + + /// Return true if the effect is allowed to be inferred on the callee, + /// which is either a FunctionDecl or BlockDecl. + /// This is only used if the effect has FE_InferrableOnCallees flag set. + /// Example: This allows nonblocking(false) to prevent inference for the + /// function. + bool canInferOnFunction(const Decl ) const; + + // Return false for success. When true is returned for a direct call, then the + // FE_InferrableOnCallees flag may trigger inference rather than an immediate + // diagnostic. Caller should be assumed to have the effect (it may not have it + // explicitly when inferring). + bool shouldDiagnoseFunctionCall(bool Direct, + ArrayRef CalleeFX) const; + + friend bool operator==(const FunctionEffect , const FunctionEffect ) { +return LHS.FKind == RHS.FKind; + } + friend bool operator!=(const FunctionEffect , const FunctionEffect ) { +return !(LHS == RHS); + } + friend bool operator<(const FunctionEffect , const FunctionEffect ) { +return LHS.FKind < RHS.FKind; + } +}; + +/// Wrap a function effect's condition expression in another struct so +/// that FunctionProtoType's TrailingObjects can treat it separately. +class FunctionEffectCondition { + Expr *Cond = nullptr; // if null, unconditional + +public: + FunctionEffectCondition() = default; + FunctionEffectCondition(Expr *E) : Cond(E) {} // implicit OK + + Expr *expr() const { return Cond; } + + bool operator==(const FunctionEffectCondition ) const { +return Cond == RHS.Cond; + } +}; + +/// A FunctionEffect plus a potential boolean expression determining whether +/// the effect is declared (e.g. nonblocking(expr)). Generally the condition +/// expression when present, is dependent. +struct FunctionEffectWithCondition { + FunctionEffect Effect; + FunctionEffectCondition Cond; + + /// Return a textual description of the effect, and its condition, if any. + std::string description() const; +}; + +struct FunctionEffectDiff { + enum class Kind { Added, Removed, ConditionMismatch }; + + FunctionEffect::Kind EffectKind; + Kind DiffKind; + FunctionEffectWithCondition Old; // invalid when Added + FunctionEffectWithCondition
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -4639,6 +4644,312 @@ class FunctionNoProtoType : public FunctionType, public llvm::FoldingSetNode { } }; +// -- + +class Decl; +class CXXMethodDecl; +struct FunctionEffectDiff; +class FunctionEffectsRef; +class FunctionEffectSet; + +/// Represents an abstract function effect, using just an enumeration describing +/// its kind. +class FunctionEffect { +public: + /// Identifies the particular effect. + enum class Kind : uint8_t { +None = 0, +NonBlocking = 1, +NonAllocating = 2, +Blocking = 3, +Allocating = 4 + }; + + /// Flags describing some behaviors of the effect. + using Flags = unsigned; + enum FlagBit : Flags { +// Can verification inspect callees' implementations? (e.g. nonblocking: +// yes, tcb+types: no). This also implies the need for 2nd-pass +// verification. +FE_InferrableOnCallees = 0x1, + +// Language constructs which effects can diagnose as disallowed. +FE_ExcludeThrow = 0x2, +FE_ExcludeCatch = 0x4, +FE_ExcludeObjCMessageSend = 0x8, +FE_ExcludeStaticLocalVars = 0x10, +FE_ExcludeThreadLocalVars = 0x20 + }; + +private: + LLVM_PREFERRED_TYPE(Kind) + unsigned FKind : 3; + + // Expansion: for hypothetical TCB+types, there could be one Kind for TCB, + // then ~16(?) bits "SubKind" to map to a specific named TCB. SubKind would + // be considered for uniqueness. + +public: + FunctionEffect() : FKind(unsigned(Kind::None)) {} + + explicit FunctionEffect(Kind K) : FKind(unsigned(K)) {} + + /// The kind of the effect. + Kind kind() const { return Kind(FKind); } + + /// Return the opposite kind, for effects which have opposites. + Kind oppositeKind() const; + + /// For serialization. + uint32_t toOpaqueInt32() const { return FKind; } + static FunctionEffect fromOpaqueInt32(uint32_t Value) { +return FunctionEffect(Kind(Value)); + } + + /// Flags describing some behaviors of the effect. + Flags flags() const { +switch (kind()) { +case Kind::NonBlocking: + return FE_InferrableOnCallees | FE_ExcludeThrow | FE_ExcludeCatch | + FE_ExcludeObjCMessageSend | FE_ExcludeStaticLocalVars | + FE_ExcludeThreadLocalVars; +case Kind::NonAllocating: + // Same as NonBlocking, except without FE_ExcludeStaticLocalVars + return FE_InferrableOnCallees | FE_ExcludeThrow | FE_ExcludeCatch | + FE_ExcludeObjCMessageSend | FE_ExcludeThreadLocalVars; +case Kind::Blocking: +case Kind::Allocating: + return 0; +case Kind::None: + break; +} +llvm_unreachable("unknown effect kind"); + } + + /// The description printed in diagnostics, e.g. 'nonblocking'. + StringRef name() const; + + /// Return true if the effect is allowed to be inferred on the callee, + /// which is either a FunctionDecl or BlockDecl. + /// This is only used if the effect has FE_InferrableOnCallees flag set. + /// Example: This allows nonblocking(false) to prevent inference for the + /// function. + bool canInferOnFunction(const Decl ) const; + + // Return false for success. When true is returned for a direct call, then the + // FE_InferrableOnCallees flag may trigger inference rather than an immediate + // diagnostic. Caller should be assumed to have the effect (it may not have it + // explicitly when inferring). + bool shouldDiagnoseFunctionCall(bool Direct, + ArrayRef CalleeFX) const; + + friend bool operator==(const FunctionEffect , const FunctionEffect ) { +return LHS.FKind == RHS.FKind; + } + friend bool operator!=(const FunctionEffect , const FunctionEffect ) { +return !(LHS == RHS); + } + friend bool operator<(const FunctionEffect , const FunctionEffect ) { +return LHS.FKind < RHS.FKind; + } +}; + +/// Wrap a function effect's condition expression in another struct so +/// that FunctionProtoType's TrailingObjects can treat it separately. +class FunctionEffectCondition { + Expr *Cond = nullptr; // if null, unconditional + +public: + FunctionEffectCondition() = default; + FunctionEffectCondition(Expr *E) : Cond(E) {} // implicit OK + + Expr *expr() const { return Cond; } + + bool operator==(const FunctionEffectCondition ) const { +return Cond == RHS.Cond; + } +}; + +/// A FunctionEffect plus a potential boolean expression determining whether +/// the effect is declared (e.g. nonblocking(expr)). Generally the condition +/// expression when present, is dependent. +struct FunctionEffectWithCondition { + FunctionEffect Effect; + FunctionEffectCondition Cond; + + /// Return a textual description of the effect, and its condition, if any. + std::string description() const; +}; + +struct FunctionEffectDiff { + enum class Kind { Added, Removed, ConditionMismatch }; + + FunctionEffect::Kind EffectKind; + Kind DiffKind; + FunctionEffectWithCondition Old; // invalid when Added + FunctionEffectWithCondition
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
https://github.com/dougsonos edited 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -5028,3 +5050,376 @@ void AutoType::Profile(llvm::FoldingSetNodeID , const ASTContext ) { Profile(ID, Context, getDeducedType(), getKeyword(), isDependentType(), getTypeConstraintConcept(), getTypeConstraintArguments()); } + +FunctionEffect::Kind FunctionEffect::oppositeKind() const { + switch (kind()) { + case Kind::NonBlocking: +return Kind::Blocking; + case Kind::Blocking: +return Kind::NonBlocking; + case Kind::NonAllocating: +return Kind::Allocating; + case Kind::Allocating: +return Kind::NonAllocating; + case Kind::None: +return Kind::None; + } + llvm_unreachable("unknown effect kind"); +} + +StringRef FunctionEffect::name() const { + switch (kind()) { + case Kind::NonBlocking: +return "nonblocking"; + case Kind::NonAllocating: +return "nonallocating"; + case Kind::Blocking: +return "blocking"; + case Kind::Allocating: +return "allocating"; + case Kind::None: +break; + } + llvm_unreachable("unknown effect kind"); +} + +bool FunctionEffectDiff::shouldDiagnoseConversion( +QualType SrcType, const FunctionEffectsRef , QualType DstType, +const FunctionEffectsRef ) const { + + switch (EffectKind) { + case FunctionEffect::Kind::NonAllocating: +// nonallocating can't be added (spoofed) during a conversion, unless we +// have nonblocking +if (DiffKind == Kind::Added) { + for (const auto : SrcFX) { +if (CFE.Effect.kind() == FunctionEffect::Kind::NonBlocking) + return false; + } +} +[[fallthrough]]; + case FunctionEffect::Kind::NonBlocking: +// nonblocking can't be added (spoofed) during a conversion. +switch (DiffKind) { +case Kind::Added: + return true; +case Kind::Removed: + return false; +case Kind::ConditionMismatch: + return true; // TODO: ??? +} + case FunctionEffect::Kind::Blocking: + case FunctionEffect::Kind::Allocating: +return false; + case FunctionEffect::Kind::None: +break; + } + llvm_unreachable("unknown effect kind"); +} + +bool FunctionEffectDiff::shouldDiagnoseRedeclaration( +const FunctionDecl , const FunctionEffectsRef , +const FunctionDecl , const FunctionEffectsRef ) const { + switch (EffectKind) { + case FunctionEffect::Kind::NonAllocating: + case FunctionEffect::Kind::NonBlocking: +// nonblocking/nonallocating can't be removed in a redeclaration +switch (DiffKind) { +case Kind::Added: + return false; // No diagnostic. +case Kind::Removed: + return true; // Issue diagnostic +case Kind::ConditionMismatch: + // All these forms of mismatches are diagnosed. + return true; +} + case FunctionEffect::Kind::Blocking: + case FunctionEffect::Kind::Allocating: +return false; + case FunctionEffect::Kind::None: +break; + } + llvm_unreachable("unknown effect kind"); +} + +FunctionEffectDiff::OverrideResult +FunctionEffectDiff::shouldDiagnoseMethodOverride( +const CXXMethodDecl , const FunctionEffectsRef , +const CXXMethodDecl , const FunctionEffectsRef ) const { + switch (EffectKind) { + case FunctionEffect::Kind::NonAllocating: + case FunctionEffect::Kind::NonBlocking: +switch (DiffKind) { + +// If added on an override, that's fine and not diagnosed. +case Kind::Added: + return OverrideResult::NoAction; + +// If missing from an override (removed), propagate from base to derived. +case Kind::Removed: + return OverrideResult::Merge; + +// If there's a mismatch involving the effect's polarity or condition, +// issue a warning. +case Kind::ConditionMismatch: + return OverrideResult::Warn; +} + + case FunctionEffect::Kind::Blocking: + case FunctionEffect::Kind::Allocating: +return OverrideResult::NoAction; + + case FunctionEffect::Kind::None: +break; + } + llvm_unreachable("unknown effect kind"); +} + +bool FunctionEffect::canInferOnFunction(const Decl ) const { + switch (kind()) { + case Kind::NonAllocating: + case Kind::NonBlocking: { +FunctionEffectsRef CalleeFX; +if (auto *FD = Callee.getAsFunction()) + CalleeFX = FD->getFunctionEffects(); +else if (auto *BD = dyn_cast()) + CalleeFX = BD->getFunctionEffects(); +else + return false; +for (const FunctionEffectWithCondition : CalleeFX) { + // nonblocking/nonallocating cannot call allocating + if (CalleeEC.Effect.kind() == Kind::Allocating) +return false; + // nonblocking cannot call blocking + if (kind() == Kind::NonBlocking && + CalleeEC.Effect.kind() == Kind::Blocking) +return false; +} + } +return true; + + case Kind::Allocating: + case Kind::Blocking: +return false; + + case Kind::None: +break; + } + llvm_unreachable("unknown effect kind"); +} + +bool FunctionEffect::shouldDiagnoseFunctionCall( +bool Direct, ArrayRef CalleeFX) const { + switch (kind()) { + case Kind::NonAllocating: + case
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
https://github.com/dougsonos deleted 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -3649,6 +3649,25 @@ FunctionProtoType::FunctionProtoType(QualType result, ArrayRef params, auto = *getTrailingObjects(); EllipsisLoc = epi.EllipsisLoc; } + + if (!epi.FunctionEffects.empty()) { +auto = *getTrailingObjects(); +// TODO: bitfield overflow? +ExtraBits.NumFunctionEffects = epi.FunctionEffects.size(); + +ArrayRef SrcFX = epi.FunctionEffects.effects(); +auto *DestFX = getTrailingObjects(); +std::copy(SrcFX.begin(), SrcFX.end(), DestFX); + +ArrayRef SrcConds = +epi.FunctionEffects.conditions(); +if (!SrcConds.empty()) { + ExtraBits.EffectsHaveConditions = true; + auto *DestConds = getTrailingObjects(); + std::copy(SrcConds.begin(), SrcConds.end(), DestConds); + addDependence(TypeDependence::DependentInstantiation); dougsonos wrote: done 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -1870,6 +1870,28 @@ bool Sema::IsFunctionConversion(QualType FromType, QualType ToType, FromFn = QT->getAs(); Changed = true; } + +// For C, when called from checkPointerTypesForAssignment, +// we need not to alter FromFn, or else even an innocuous cast +// like dropping effects will fail. In C++ however we do want to +// alter FromFn. TODO: Is this correct? dougsonos wrote: Yeah, I'm still not 100% clear here 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
https://github.com/dougsonos edited 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -5028,3 +5050,376 @@ void AutoType::Profile(llvm::FoldingSetNodeID , const ASTContext ) { Profile(ID, Context, getDeducedType(), getKeyword(), isDependentType(), getTypeConstraintConcept(), getTypeConstraintArguments()); } + +FunctionEffect::Kind FunctionEffect::oppositeKind() const { + switch (kind()) { + case Kind::NonBlocking: +return Kind::Blocking; + case Kind::Blocking: +return Kind::NonBlocking; + case Kind::NonAllocating: +return Kind::Allocating; + case Kind::Allocating: +return Kind::NonAllocating; + case Kind::None: +return Kind::None; + } + llvm_unreachable("unknown effect kind"); +} + +StringRef FunctionEffect::name() const { + switch (kind()) { + case Kind::NonBlocking: +return "nonblocking"; + case Kind::NonAllocating: +return "nonallocating"; + case Kind::Blocking: +return "blocking"; + case Kind::Allocating: +return "allocating"; + case Kind::None: +break; + } + llvm_unreachable("unknown effect kind"); +} + +bool FunctionEffectDiff::shouldDiagnoseConversion( +QualType SrcType, const FunctionEffectsRef , QualType DstType, +const FunctionEffectsRef ) const { + + switch (EffectKind) { + case FunctionEffect::Kind::NonAllocating: +// nonallocating can't be added (spoofed) during a conversion, unless we +// have nonblocking +if (DiffKind == Kind::Added) { + for (const auto : SrcFX) { +if (CFE.Effect.kind() == FunctionEffect::Kind::NonBlocking) + return false; + } +} +[[fallthrough]]; + case FunctionEffect::Kind::NonBlocking: +// nonblocking can't be added (spoofed) during a conversion. +switch (DiffKind) { +case Kind::Added: + return true; +case Kind::Removed: + return false; +case Kind::ConditionMismatch: + return true; // TODO: ??? +} + case FunctionEffect::Kind::Blocking: + case FunctionEffect::Kind::Allocating: +return false; + case FunctionEffect::Kind::None: +break; + } + llvm_unreachable("unknown effect kind"); +} + +bool FunctionEffectDiff::shouldDiagnoseRedeclaration( +const FunctionDecl , const FunctionEffectsRef , +const FunctionDecl , const FunctionEffectsRef ) const { + switch (EffectKind) { + case FunctionEffect::Kind::NonAllocating: + case FunctionEffect::Kind::NonBlocking: +// nonblocking/nonallocating can't be removed in a redeclaration +switch (DiffKind) { +case Kind::Added: + return false; // No diagnostic. +case Kind::Removed: + return true; // Issue diagnostic +case Kind::ConditionMismatch: + // All these forms of mismatches are diagnosed. + return true; +} + case FunctionEffect::Kind::Blocking: + case FunctionEffect::Kind::Allocating: +return false; + case FunctionEffect::Kind::None: +break; + } + llvm_unreachable("unknown effect kind"); +} + +FunctionEffectDiff::OverrideResult +FunctionEffectDiff::shouldDiagnoseMethodOverride( +const CXXMethodDecl , const FunctionEffectsRef , +const CXXMethodDecl , const FunctionEffectsRef ) const { + switch (EffectKind) { + case FunctionEffect::Kind::NonAllocating: + case FunctionEffect::Kind::NonBlocking: +switch (DiffKind) { + +// If added on an override, that's fine and not diagnosed. +case Kind::Added: + return OverrideResult::NoAction; + +// If missing from an override (removed), propagate from base to derived. +case Kind::Removed: + return OverrideResult::Merge; + +// If there's a mismatch involving the effect's polarity or condition, +// issue a warning. +case Kind::ConditionMismatch: + return OverrideResult::Warn; +} + + case FunctionEffect::Kind::Blocking: + case FunctionEffect::Kind::Allocating: +return OverrideResult::NoAction; + + case FunctionEffect::Kind::None: +break; + } + llvm_unreachable("unknown effect kind"); +} + +bool FunctionEffect::canInferOnFunction(const Decl ) const { + switch (kind()) { + case Kind::NonAllocating: + case Kind::NonBlocking: { +FunctionEffectsRef CalleeFX; +if (auto *FD = Callee.getAsFunction()) + CalleeFX = FD->getFunctionEffects(); +else if (auto *BD = dyn_cast()) + CalleeFX = BD->getFunctionEffects(); +else + return false; +for (const FunctionEffectWithCondition : CalleeFX) { + // nonblocking/nonallocating cannot call allocating + if (CalleeEC.Effect.kind() == Kind::Allocating) +return false; + // nonblocking cannot call blocking + if (kind() == Kind::NonBlocking && + CalleeEC.Effect.kind() == Kind::Blocking) +return false; +} + } +return true; + + case Kind::Allocating: + case Kind::Blocking: +return false; + + case Kind::None: +break; + } + llvm_unreachable("unknown effect kind"); +} + +bool FunctionEffect::shouldDiagnoseFunctionCall( +bool Direct, ArrayRef CalleeFX) const { + switch (kind()) { + case Kind::NonAllocating: + case
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -10510,6 +10512,8 @@ QualType ASTContext::mergeFunctionTypes(QualType lhs, QualType rhs, if (lproto->getMethodQuals() != rproto->getMethodQuals()) return {}; +// TODO: (nonblocking) Does anything need to be done with FunctionEffects? + dougsonos wrote: I managed to refresh my memory of when this can be called -- it's in C, for redeclarations with attribute mismatches, and in situations like trying to build a type for the result of a conditional expression. There's a good example with how this method handles `noreturn`, which I'll copy. 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -7963,6 +7967,148 @@ static Attr *getCCTypeAttr(ASTContext , ParsedAttr ) { llvm_unreachable("unexpected attribute kind!"); } +ExprResult Sema::ActOnEffectExpression(Expr *CondExpr, FunctionEffectMode , + bool RequireConstexpr) { + // see checkFunctionConditionAttr, Sema::CheckCXXBooleanCondition + if (RequireConstexpr || !CondExpr->isTypeDependent()) { +ExprResult E = PerformContextuallyConvertToBool(CondExpr); +if (E.isInvalid()) + return E; +CondExpr = E.get(); +if (RequireConstexpr || !CondExpr->isValueDependent()) { dougsonos wrote: Thanks. I found some examples of other attributes that are handled more simply, as you describe, will work to simplify. 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
https://github.com/dougsonos edited 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -4639,6 +4644,312 @@ class FunctionNoProtoType : public FunctionType, public llvm::FoldingSetNode { } }; +// -- + +class Decl; +class CXXMethodDecl; +struct FunctionEffectDiff; +class FunctionEffectsRef; +class FunctionEffectSet; + +/// Represents an abstract function effect, using just an enumeration describing +/// its kind. +class FunctionEffect { +public: + /// Identifies the particular effect. + enum class Kind : uint8_t { +None = 0, +NonBlocking = 1, +NonAllocating = 2, +Blocking = 3, +Allocating = 4 + }; + + /// Flags describing some behaviors of the effect. + using Flags = unsigned; + enum FlagBit : Flags { +// Can verification inspect callees' implementations? (e.g. nonblocking: +// yes, tcb+types: no). This also implies the need for 2nd-pass +// verification. +FE_InferrableOnCallees = 0x1, + +// Language constructs which effects can diagnose as disallowed. +FE_ExcludeThrow = 0x2, +FE_ExcludeCatch = 0x4, +FE_ExcludeObjCMessageSend = 0x8, +FE_ExcludeStaticLocalVars = 0x10, +FE_ExcludeThreadLocalVars = 0x20 + }; + +private: + LLVM_PREFERRED_TYPE(Kind) + unsigned FKind : 3; + + // Expansion: for hypothetical TCB+types, there could be one Kind for TCB, + // then ~16(?) bits "SubKind" to map to a specific named TCB. SubKind would + // be considered for uniqueness. + +public: + FunctionEffect() : FKind(unsigned(Kind::None)) {} + + explicit FunctionEffect(Kind K) : FKind(unsigned(K)) {} + + /// The kind of the effect. + Kind kind() const { return Kind(FKind); } + + /// Return the opposite kind, for effects which have opposites. + Kind oppositeKind() const; + + /// For serialization. + uint32_t toOpaqueInt32() const { return FKind; } + static FunctionEffect fromOpaqueInt32(uint32_t Value) { +return FunctionEffect(Kind(Value)); + } + + /// Flags describing some behaviors of the effect. + Flags flags() const { +switch (kind()) { +case Kind::NonBlocking: + return FE_InferrableOnCallees | FE_ExcludeThrow | FE_ExcludeCatch | + FE_ExcludeObjCMessageSend | FE_ExcludeStaticLocalVars | + FE_ExcludeThreadLocalVars; +case Kind::NonAllocating: + // Same as NonBlocking, except without FE_ExcludeStaticLocalVars + return FE_InferrableOnCallees | FE_ExcludeThrow | FE_ExcludeCatch | + FE_ExcludeObjCMessageSend | FE_ExcludeThreadLocalVars; +case Kind::Blocking: +case Kind::Allocating: + return 0; +case Kind::None: + break; +} +llvm_unreachable("unknown effect kind"); + } + + /// The description printed in diagnostics, e.g. 'nonblocking'. + StringRef name() const; + + /// Return true if the effect is allowed to be inferred on the callee, + /// which is either a FunctionDecl or BlockDecl. + /// This is only used if the effect has FE_InferrableOnCallees flag set. + /// Example: This allows nonblocking(false) to prevent inference for the + /// function. + bool canInferOnFunction(const Decl ) const; + + // Return false for success. When true is returned for a direct call, then the + // FE_InferrableOnCallees flag may trigger inference rather than an immediate + // diagnostic. Caller should be assumed to have the effect (it may not have it + // explicitly when inferring). + bool shouldDiagnoseFunctionCall(bool Direct, + ArrayRef CalleeFX) const; + + friend bool operator==(const FunctionEffect , const FunctionEffect ) { +return LHS.FKind == RHS.FKind; + } + friend bool operator!=(const FunctionEffect , const FunctionEffect ) { +return !(LHS == RHS); + } + friend bool operator<(const FunctionEffect , const FunctionEffect ) { +return LHS.FKind < RHS.FKind; + } +}; + +/// Wrap a function effect's condition expression in another struct so +/// that FunctionProtoType's TrailingObjects can treat it separately. +class FunctionEffectCondition { + Expr *Cond = nullptr; // if null, unconditional + +public: + FunctionEffectCondition() = default; + FunctionEffectCondition(Expr *E) : Cond(E) {} // implicit OK + + Expr *expr() const { return Cond; } + + bool operator==(const FunctionEffectCondition ) const { +return Cond == RHS.Cond; + } +}; + +/// A FunctionEffect plus a potential boolean expression determining whether +/// the effect is declared (e.g. nonblocking(expr)). Generally the condition +/// expression when present, is dependent. +struct FunctionEffectWithCondition { + FunctionEffect Effect; + FunctionEffectCondition Cond; + + /// Return a textual description of the effect, and its condition, if any. + std::string description() const; +}; + +struct FunctionEffectDiff { + enum class Kind { Added, Removed, ConditionMismatch }; + + FunctionEffect::Kind EffectKind; + Kind DiffKind; + FunctionEffectWithCondition Old; // invalid when Added + FunctionEffectWithCondition
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -768,6 +776,18 @@ class Sema final : public SemaBase { /// Warn when implicitly casting 0 to nullptr. void diagnoseZeroToNullptrConversion(CastKind Kind, const Expr *E); + // - function effects --- + + /// Warn when implicitly changing function effects. + void diagnoseFunctionEffectConversion(QualType DstType, QualType SrcType, +SourceLocation Loc); + + /// Try to parse the conditional expression attached to an effect attribute + /// (e.g. 'nonblocking'). (c.f. Sema::ActOnNoexceptSpec). If RequireConstexpr, + /// then this will fail if the expression is dependent. + ExprResult ActOnEffectExpression(Expr *CondExpr, FunctionEffectMode , + bool RequireConstexpr = false); dougsonos wrote: Will check but I suspect it's used in Part Two 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -7963,6 +7967,148 @@ static Attr *getCCTypeAttr(ASTContext , ParsedAttr ) { llvm_unreachable("unexpected attribute kind!"); } +ExprResult Sema::ActOnEffectExpression(Expr *CondExpr, FunctionEffectMode , + bool RequireConstexpr) { + // see checkFunctionConditionAttr, Sema::CheckCXXBooleanCondition + if (RequireConstexpr || !CondExpr->isTypeDependent()) { +ExprResult E = PerformContextuallyConvertToBool(CondExpr); +if (E.isInvalid()) dougsonos wrote: This was copied from `Sema::CheckCXXBooleanCondition`; is it incorrect too? 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -1870,6 +1870,28 @@ bool Sema::IsFunctionConversion(QualType FromType, QualType ToType, FromFn = QT->getAs(); Changed = true; } + +// For C, when called from checkPointerTypesForAssignment, +// we need not to alter FromFn, or else even an innocuous cast +// like dropping effects will fail. In C++ however we do want to +// alter FromFn. TODO: Is this correct? +if (getLangOpts().CPlusPlus) { + FromFPT = + dyn_cast(FromFn); // in case FromFn changed above dougsonos wrote: Interesting, I copied this verbatim from just above: ``` // Drop 'noexcept' if not present in target type. if (const auto *FromFPT = dyn_cast(FromFn)) { const auto *ToFPT = cast(ToFn); ``` Is this wrong too? 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -5028,3 +5050,376 @@ void AutoType::Profile(llvm::FoldingSetNodeID , const ASTContext ) { Profile(ID, Context, getDeducedType(), getKeyword(), isDependentType(), getTypeConstraintConcept(), getTypeConstraintArguments()); } + +FunctionEffect::Kind FunctionEffect::oppositeKind() const { + switch (kind()) { + case Kind::NonBlocking: +return Kind::Blocking; + case Kind::Blocking: +return Kind::NonBlocking; + case Kind::NonAllocating: +return Kind::Allocating; + case Kind::Allocating: +return Kind::NonAllocating; + case Kind::None: +return Kind::None; + } + llvm_unreachable("unknown effect kind"); +} + +StringRef FunctionEffect::name() const { + switch (kind()) { + case Kind::NonBlocking: +return "nonblocking"; + case Kind::NonAllocating: +return "nonallocating"; + case Kind::Blocking: +return "blocking"; + case Kind::Allocating: +return "allocating"; + case Kind::None: +break; + } + llvm_unreachable("unknown effect kind"); +} + +bool FunctionEffectDiff::shouldDiagnoseConversion( +QualType SrcType, const FunctionEffectsRef , QualType DstType, +const FunctionEffectsRef ) const { + + switch (EffectKind) { + case FunctionEffect::Kind::NonAllocating: +// nonallocating can't be added (spoofed) during a conversion, unless we +// have nonblocking +if (DiffKind == Kind::Added) { + for (const auto : SrcFX) { +if (CFE.Effect.kind() == FunctionEffect::Kind::NonBlocking) + return false; + } +} +[[fallthrough]]; + case FunctionEffect::Kind::NonBlocking: +// nonblocking can't be added (spoofed) during a conversion. +switch (DiffKind) { +case Kind::Added: + return true; +case Kind::Removed: + return false; +case Kind::ConditionMismatch: + return true; // TODO: ??? +} + case FunctionEffect::Kind::Blocking: + case FunctionEffect::Kind::Allocating: +return false; + case FunctionEffect::Kind::None: +break; + } + llvm_unreachable("unknown effect kind"); +} + +bool FunctionEffectDiff::shouldDiagnoseRedeclaration( +const FunctionDecl , const FunctionEffectsRef , +const FunctionDecl , const FunctionEffectsRef ) const { + switch (EffectKind) { + case FunctionEffect::Kind::NonAllocating: + case FunctionEffect::Kind::NonBlocking: +// nonblocking/nonallocating can't be removed in a redeclaration +switch (DiffKind) { +case Kind::Added: + return false; // No diagnostic. +case Kind::Removed: + return true; // Issue diagnostic +case Kind::ConditionMismatch: + // All these forms of mismatches are diagnosed. + return true; +} + case FunctionEffect::Kind::Blocking: + case FunctionEffect::Kind::Allocating: +return false; + case FunctionEffect::Kind::None: +break; + } + llvm_unreachable("unknown effect kind"); +} + +FunctionEffectDiff::OverrideResult +FunctionEffectDiff::shouldDiagnoseMethodOverride( +const CXXMethodDecl , const FunctionEffectsRef , +const CXXMethodDecl , const FunctionEffectsRef ) const { + switch (EffectKind) { + case FunctionEffect::Kind::NonAllocating: + case FunctionEffect::Kind::NonBlocking: +switch (DiffKind) { + +// If added on an override, that's fine and not diagnosed. +case Kind::Added: + return OverrideResult::NoAction; + +// If missing from an override (removed), propagate from base to derived. +case Kind::Removed: + return OverrideResult::Merge; + +// If there's a mismatch involving the effect's polarity or condition, +// issue a warning. +case Kind::ConditionMismatch: + return OverrideResult::Warn; +} + + case FunctionEffect::Kind::Blocking: + case FunctionEffect::Kind::Allocating: +return OverrideResult::NoAction; + + case FunctionEffect::Kind::None: +break; + } + llvm_unreachable("unknown effect kind"); +} + +bool FunctionEffect::canInferOnFunction(const Decl ) const { + switch (kind()) { + case Kind::NonAllocating: + case Kind::NonBlocking: { +FunctionEffectsRef CalleeFX; +if (auto *FD = Callee.getAsFunction()) + CalleeFX = FD->getFunctionEffects(); +else if (auto *BD = dyn_cast()) + CalleeFX = BD->getFunctionEffects(); +else + return false; +for (const FunctionEffectWithCondition : CalleeFX) { + // nonblocking/nonallocating cannot call allocating + if (CalleeEC.Effect.kind() == Kind::Allocating) +return false; + // nonblocking cannot call blocking + if (kind() == Kind::NonBlocking && + CalleeEC.Effect.kind() == Kind::Blocking) +return false; +} + } +return true; + + case Kind::Allocating: + case Kind::Blocking: +return false; + + case Kind::None: +break; + } + llvm_unreachable("unknown effect kind"); +} + +bool FunctionEffect::shouldDiagnoseFunctionCall( +bool Direct, ArrayRef CalleeFX) const { + switch (kind()) { + case Kind::NonAllocating: + case
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -7963,6 +7967,148 @@ static Attr *getCCTypeAttr(ASTContext , ParsedAttr ) { llvm_unreachable("unexpected attribute kind!"); } +ExprResult Sema::ActOnEffectExpression(Expr *CondExpr, FunctionEffectMode , + bool RequireConstexpr) { + // see checkFunctionConditionAttr, Sema::CheckCXXBooleanCondition + if (RequireConstexpr || !CondExpr->isTypeDependent()) { +ExprResult E = PerformContextuallyConvertToBool(CondExpr); +if (E.isInvalid()) + return E; +CondExpr = E.get(); +if (RequireConstexpr || !CondExpr->isValueDependent()) { + llvm::APSInt CondInt; + E = VerifyIntegerConstantExpression( + E.get(), , + // TODO: have our own diagnostic + diag::err_constexpr_if_condition_expression_is_not_constant); + if (E.isInvalid()) { dougsonos wrote: that's clang-format's choice 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
https://github.com/dougsonos edited 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -18347,7 +18347,7 @@ void Sema::SetFunctionBodyKind(Decl *D, SourceLocation Loc, FnBodyKind BodyKind, } } -bool Sema::CheckOverridingFunctionAttributes(const CXXMethodDecl *New, +bool Sema::CheckOverridingFunctionAttributes(CXXMethodDecl *New, dougsonos wrote: I did have to make this new Decl mutable, because the verification can implicitly propagate (certain) effects from the base virtual method to its overrides. (Originally I had done this with an ugly `const_cast` but we discussed this and chose instead to make the parameter mutable.) 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
dougsonos wrote: > So seems like I was just premature, makes sense to break this in to pieces > :). Am I right to assume that these warnings will be back, just in PR 2? Yes, you can revert c18b77459fb34482f169323de9a81142a922b6a7 to bring the caller/callee diagnostics back. 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -8057,3 +8057,70 @@ requirement: } }]; } + +def DocCatNonBlockingNonAllocating : DocumentationCategory<"Performance Constraint Attributes"> { + let Content = [{ dougsonos wrote: Done in the next commit(s) I push 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
dougsonos wrote: Hi Chris, Currently I had only envisioned that `nonblocking` would have the optional conditional expression parameter, so `blocking(false)` should generate an error. I had mistakenly thought that the parser would take care of this via the ways that the attributes are declared. Is there a compelling reason to support `blocking(false)` as a synonym for `nonblocking`? beyond symmetry? If not, I'll just fix the argument count check. 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
https://github.com/dougsonos ready_for_review 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
https://github.com/dougsonos edited 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
https://github.com/dougsonos edited 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
https://github.com/dougsonos edited 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
dougsonos wrote: I think this is now as complete as I can make it without further feedback. Should I update the description and make it no longer a draft? Squash and make a new PR? Thanks for all the help @Sirraide ! 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -7973,3 +7973,20 @@ requirement: } }]; } + +def NoLockNoAllocDocs : Documentation { + let Category = DocCatType; + let Content = [{ +The ``nolock`` and ``noalloc`` attributes can be attached to functions, blocks, +function pointers, lambdas, and member functions. The attributes identify code +which must not allocate memory or lock, and the compiler uses the attributes to +verify these requirements. + +Like ``noexcept``, ``nolock`` and ``noalloc`` have an optional argument, a +compile-time constant boolean expression. By default, the argument is true, so +``[[clang::nolock(true)]]`` is equivalent to ``[[clang::nolock]]``, and declares +the function type as never locking. + +TODO: how much of the RFC to include here? Make it a separate page? dougsonos wrote: I made a documentation category for the four attributes and wrote a few paragraphs that tries to boil things down to essentials (and limited to the smaller scope of this first commit). 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
https://github.com/dougsonos edited 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
https://github.com/dougsonos edited 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -671,6 +684,10 @@ ExprResult Sema::ImpCastExprToType(Expr *E, QualType Ty, diagnoseNullableToNonnullConversion(Ty, E->getType(), E->getBeginLoc()); diagnoseZeroToNullptrConversion(Kind, E); + if (!isCast(CCK) && + !E->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNotNull)) { dougsonos wrote: This seems to be causing some failed tests. `isNullPointerConstant` calls `Expr::EvaluateKnownConstInt` where this assertion fails: ``` assert(!isValueDependent() && "Expression evaluator can't be called on a dependent expression."); ``` 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
https://github.com/dougsonos edited 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
[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
@@ -27,6 +27,8 @@ #include "clang/AST/StmtObjC.h" #include "clang/AST/StmtVisitor.h" #include "clang/AST/Type.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" dougsonos wrote: The ASTMatcher is only used in some debug-only code. It uses an AST traversal to find all the Decls to verify and then compares it against Sema's `DeclsWithEffectsToVerify`. I'm convinced now that Sema isn't missing anything so I'll remove the dependency. 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
[clang] [llvm] [clang backend] In AArch64's DataLayout, specify a minimum function alignment of 4. (PR #90702)
https://github.com/dougsonos deleted https://github.com/llvm/llvm-project/pull/90702 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [llvm] [clang backend] In AArch64's DataLayout, specify a minimum function alignment of 4. (PR #90702)
https://github.com/dougsonos updated https://github.com/llvm/llvm-project/pull/90702 >From 4c312af7af5d0e419269c5b2304b0f898363bb85 Mon Sep 17 00:00:00 2001 From: Doug Wyatt Date: Tue, 30 Apr 2024 21:43:16 -0700 Subject: [PATCH 1/5] In AArch64's DataLayout, specify a minimum function alignment of 4. This addresses an issue where the explicit alignment of 2 (for C++ ABI reasons) was being propagated to the back end and causing under-aligned functions (in special sections). (#90358) --- clang/lib/Basic/Targets/AArch64.cpp | 12 ++-- llvm/lib/IR/AutoUpgrade.cpp | 8 llvm/lib/Target/AArch64/AArch64TargetMachine.cpp | 8 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/clang/lib/Basic/Targets/AArch64.cpp b/clang/lib/Basic/Targets/AArch64.cpp index c8d243a8fb7aea..a5dd803f636b90 100644 --- a/clang/lib/Basic/Targets/AArch64.cpp +++ b/clang/lib/Basic/Targets/AArch64.cpp @@ -1480,11 +1480,11 @@ AArch64leTargetInfo::AArch64leTargetInfo(const llvm::Triple , void AArch64leTargetInfo::setDataLayout() { if (getTriple().isOSBinFormatMachO()) { if(getTriple().isArch32Bit()) - resetDataLayout("e-m:o-p:32:32-i64:64-i128:128-n32:64-S128", "_"); + resetDataLayout("e-m:o-p:32:32-Fn32-i64:64-i128:128-n32:64-S128", "_"); else - resetDataLayout("e-m:o-i64:64-i128:128-n32:64-S128", "_"); + resetDataLayout("e-m:o-Fn32-i64:64-i128:128-n32:64-S128", "_"); } else -resetDataLayout("e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"); + resetDataLayout("e-m:e-Fn32-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"); } void AArch64leTargetInfo::getTargetDefines(const LangOptions , @@ -1507,7 +1507,7 @@ void AArch64beTargetInfo::getTargetDefines(const LangOptions , void AArch64beTargetInfo::setDataLayout() { assert(!getTriple().isOSBinFormatMachO()); - resetDataLayout("E-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"); + resetDataLayout("E-m:e-Fn32-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"); } WindowsARM64TargetInfo::WindowsARM64TargetInfo(const llvm::Triple , @@ -1530,8 +1530,8 @@ WindowsARM64TargetInfo::WindowsARM64TargetInfo(const llvm::Triple , void WindowsARM64TargetInfo::setDataLayout() { resetDataLayout(Triple.isOSBinFormatMachO() - ? "e-m:o-i64:64-i128:128-n32:64-S128" - : "e-m:w-p:64:64-i32:32-i64:64-i128:128-n32:64-S128", + ? "e-m:o-Fn32-i64:64-i128:128-n32:64-S128" + : "e-m:w-p:64:64-Fn32-i32:32-i64:64-i128:128-n32:64-S128", Triple.isOSBinFormatMachO() ? "_" : ""); } diff --git a/llvm/lib/IR/AutoUpgrade.cpp b/llvm/lib/IR/AutoUpgrade.cpp index 634b2dd5119e8d..eed946dc36580e 100644 --- a/llvm/lib/IR/AutoUpgrade.cpp +++ b/llvm/lib/IR/AutoUpgrade.cpp @@ -5387,6 +5387,14 @@ std::string llvm::UpgradeDataLayoutString(StringRef DL, StringRef TT) { return Res; } + // AArch64 data layout upgrades. + if (T.isAArch64()) { +// Add "-Fn32" +if (!DL.contains("-Fn32")) + Res.append("-Fn32"); +return Res; + } + if (!T.isX86()) return Res; diff --git a/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp b/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp index 7ef78cbba352a5..4ff5fb94162a93 100644 --- a/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp +++ b/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp @@ -275,15 +275,15 @@ static std::string computeDataLayout(const Triple , bool LittleEndian) { if (TT.isOSBinFormatMachO()) { if (TT.getArch() == Triple::aarch64_32) - return "e-m:o-p:32:32-i64:64-i128:128-n32:64-S128"; -return "e-m:o-i64:64-i128:128-n32:64-S128"; + return "e-m:o-p:32:32-Fn32-i64:64-i128:128-n32:64-S128"; +return "e-m:o-Fn32-i64:64-i128:128-n32:64-S128"; } if (TT.isOSBinFormatCOFF()) -return "e-m:w-p:64:64-i32:32-i64:64-i128:128-n32:64-S128"; +return "e-m:w-p:64:64-Fn32-i32:32-i64:64-i128:128-n32:64-S128"; std::string Endian = LittleEndian ? "e" : "E"; std::string Ptr32 = TT.getEnvironment() == Triple::GNUILP32 ? "-p:32:32" : ""; return Endian + "-m:e" + Ptr32 + - "-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"; + "-Fn32-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"; } static StringRef computeDefaultCPU(const Triple , StringRef CPU) { >From 5a2b9e1b0444c52cd7f7cf9a3ddbb573403101a5 Mon Sep 17 00:00:00 2001 From: Doug Wyatt Date: Wed, 1 May 2024 07:27:11 -0700 Subject: [PATCH 2/5] Move "-Fn32" to the end of the data layout strings, to make the auto-upgrade simplest. --- clang/lib/Basic/Targets/AArch64.cpp | 12 ++-- llvm/lib/Target/AArch64/AArch64TargetMachine.cpp | 8 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/clang/lib/Basic/Targets/AArch64.cpp b/clang/lib/Basic/Targets/AArch64.cpp index a5dd803f636b90..1a02520d7bd1f8 100644 ---