[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)

2024-06-20 Thread Doug Wyatt via cfe-commits


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

2024-06-20 Thread Doug Wyatt via cfe-commits


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

2024-06-20 Thread Doug Wyatt via cfe-commits

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)

2024-06-20 Thread Doug Wyatt via cfe-commits


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

2024-06-20 Thread Doug Wyatt via cfe-commits


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

2024-06-18 Thread Doug Wyatt via cfe-commits


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

2024-06-18 Thread Doug Wyatt via cfe-commits


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

2024-06-18 Thread Doug Wyatt via cfe-commits


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

2024-06-18 Thread Doug Wyatt via cfe-commits


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

2024-06-18 Thread Doug Wyatt via cfe-commits

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)

2024-06-18 Thread Doug Wyatt via cfe-commits


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

2024-05-30 Thread Doug Wyatt via cfe-commits

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)

2024-05-27 Thread Doug Wyatt via cfe-commits


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

2024-05-27 Thread Doug Wyatt via cfe-commits


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

2024-05-27 Thread Doug Wyatt via cfe-commits

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)

2024-05-24 Thread Doug Wyatt via cfe-commits

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)

2024-05-24 Thread Doug Wyatt via cfe-commits

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)

2024-05-24 Thread Doug Wyatt via cfe-commits


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

2024-05-22 Thread Doug Wyatt via cfe-commits

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)

2024-05-22 Thread Doug Wyatt via cfe-commits


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

2024-05-22 Thread Doug Wyatt via cfe-commits


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

2024-05-22 Thread Doug Wyatt via cfe-commits


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

2024-05-22 Thread Doug Wyatt via cfe-commits


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

2024-05-22 Thread Doug Wyatt via cfe-commits


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

2024-05-22 Thread Doug Wyatt via cfe-commits


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

2024-05-22 Thread Doug Wyatt via cfe-commits


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

2024-05-22 Thread Doug Wyatt via cfe-commits


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

2024-05-22 Thread Doug Wyatt via cfe-commits


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

2024-05-22 Thread Doug Wyatt via cfe-commits


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

2024-05-22 Thread Doug Wyatt via cfe-commits


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

2024-05-22 Thread Doug Wyatt via cfe-commits


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

2024-05-22 Thread Doug Wyatt via cfe-commits


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

2024-05-22 Thread Doug Wyatt via cfe-commits


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

2024-05-21 Thread Doug Wyatt via cfe-commits


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

2024-05-21 Thread Doug Wyatt via cfe-commits


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

2024-05-21 Thread Doug Wyatt via cfe-commits

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)

2024-05-21 Thread Doug Wyatt via cfe-commits


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

2024-05-21 Thread Doug Wyatt via cfe-commits


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

2024-05-21 Thread Doug Wyatt via cfe-commits


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

2024-05-21 Thread Doug Wyatt via cfe-commits


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

2024-05-18 Thread Doug Wyatt via cfe-commits


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

2024-05-17 Thread Doug Wyatt via cfe-commits


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

2024-05-17 Thread Doug Wyatt via cfe-commits


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

2024-05-16 Thread Doug Wyatt via cfe-commits


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

2024-05-16 Thread Doug Wyatt via cfe-commits


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

2024-05-16 Thread Doug Wyatt via cfe-commits


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

2024-05-16 Thread Doug Wyatt via cfe-commits


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

2024-05-16 Thread Doug Wyatt via cfe-commits


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

2024-05-16 Thread Doug Wyatt via cfe-commits


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

2024-05-16 Thread Doug Wyatt via cfe-commits


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

2024-05-16 Thread Doug Wyatt via cfe-commits


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

2024-05-16 Thread Doug Wyatt via cfe-commits


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

2024-05-16 Thread Doug Wyatt via cfe-commits


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

2024-05-16 Thread Doug Wyatt via cfe-commits


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

2024-05-06 Thread Doug Wyatt via cfe-commits


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

2024-05-06 Thread Doug Wyatt via cfe-commits


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

2024-05-06 Thread Doug Wyatt via cfe-commits


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

2024-05-06 Thread Doug Wyatt via cfe-commits


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

2024-05-06 Thread Doug Wyatt via cfe-commits


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

2024-05-06 Thread Doug Wyatt via cfe-commits


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

2024-05-05 Thread Doug Wyatt via cfe-commits

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)

2024-05-05 Thread Doug Wyatt via cfe-commits

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)

2024-05-05 Thread Doug Wyatt via cfe-commits


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

2024-05-05 Thread Doug Wyatt via cfe-commits


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

2024-05-05 Thread Doug Wyatt via cfe-commits


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

2024-05-05 Thread Doug Wyatt via cfe-commits


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

2024-05-04 Thread Doug Wyatt via cfe-commits

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)

2024-05-04 Thread Doug Wyatt via cfe-commits


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

2024-05-04 Thread Doug Wyatt via cfe-commits

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)

2024-05-04 Thread Doug Wyatt via cfe-commits


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

2024-05-04 Thread Doug Wyatt via cfe-commits


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

2024-05-04 Thread Doug Wyatt via cfe-commits

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)

2024-05-04 Thread Doug Wyatt via cfe-commits


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

2024-05-04 Thread Doug Wyatt via cfe-commits


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

2024-05-04 Thread Doug Wyatt via cfe-commits


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

2024-05-04 Thread Doug Wyatt via cfe-commits

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)

2024-05-04 Thread Doug Wyatt via cfe-commits


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

2024-05-04 Thread Doug Wyatt via cfe-commits


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

2024-05-04 Thread Doug Wyatt via cfe-commits


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

2024-05-04 Thread Doug Wyatt via cfe-commits


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

2024-05-04 Thread Doug Wyatt via cfe-commits


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

2024-05-04 Thread Doug Wyatt via cfe-commits


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

2024-05-04 Thread Doug Wyatt via cfe-commits

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)

2024-05-04 Thread Doug Wyatt via cfe-commits


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

2024-05-04 Thread Doug Wyatt via cfe-commits

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)

2024-05-03 Thread Doug Wyatt via cfe-commits


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

2024-05-03 Thread Doug Wyatt via cfe-commits

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)

2024-05-03 Thread Doug Wyatt via cfe-commits

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)

2024-05-03 Thread Doug Wyatt via cfe-commits

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)

2024-05-03 Thread Doug Wyatt via cfe-commits

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)

2024-05-03 Thread Doug Wyatt via cfe-commits

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)

2024-05-02 Thread Doug Wyatt via cfe-commits

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)

2024-05-02 Thread Doug Wyatt via cfe-commits


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

2024-05-02 Thread Doug Wyatt via cfe-commits

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)

2024-05-02 Thread Doug Wyatt via cfe-commits

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)

2024-05-02 Thread Doug Wyatt via cfe-commits


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

2024-05-02 Thread Doug Wyatt via cfe-commits

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)

2024-05-02 Thread Doug Wyatt via cfe-commits


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

2024-05-02 Thread Doug Wyatt via cfe-commits

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)

2024-05-01 Thread Doug Wyatt via cfe-commits

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

  1   2   3   >