[clang] [Clang] SemaFunctionEffects: When verifying a function, ignore any conditional noexcept expression. (PR #115342)

2024-11-10 Thread Doug Wyatt via cfe-commits

https://github.com/dougsonos closed 
https://github.com/llvm/llvm-project/pull/115342
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang] SemaFunctionEffects: When verifying a function, ignore any conditional noexcept expression. (PR #115342)

2024-11-08 Thread Doug Wyatt via cfe-commits

https://github.com/dougsonos updated 
https://github.com/llvm/llvm-project/pull/115342

>From a0b6093fcf24ade7ce9ee0c3d65f679a41751225 Mon Sep 17 00:00:00 2001
From: Doug Wyatt 
Date: Tue, 5 Nov 2024 13:35:50 -0800
Subject: [PATCH 1/2] [Clang] SemaFunctionEffects: When verifying a function,
 ignore any conditional noexcept expression.

---
 clang/lib/Sema/SemaFunctionEffects.cpp| 19 +--
 .../Sema/attr-nonblocking-constraints.cpp | 12 ++--
 2 files changed, 27 insertions(+), 4 deletions(-)

diff --git a/clang/lib/Sema/SemaFunctionEffects.cpp 
b/clang/lib/Sema/SemaFunctionEffects.cpp
index ab728f24d8a271..70f6f9b6784cd8 100644
--- a/clang/lib/Sema/SemaFunctionEffects.cpp
+++ b/clang/lib/Sema/SemaFunctionEffects.cpp
@@ -972,6 +972,7 @@ class Analyzer {
 CallableInfo &CurrentCaller;
 ViolationSite VSite;
 const Expr *TrailingRequiresClause = nullptr;
+const Expr *NoexceptExpr = nullptr;
 
 FunctionBodyASTVisitor(Analyzer &Outer,
PendingFunctionAnalysis &CurrentFunction,
@@ -986,9 +987,22 @@ class Analyzer {
   if (auto *Dtor = dyn_cast(CurrentCaller.CDecl))
 followDestructor(dyn_cast(Dtor->getParent()), Dtor);
 
-  if (auto *FD = dyn_cast(CurrentCaller.CDecl))
+  if (auto *FD = dyn_cast(CurrentCaller.CDecl)) {
 TrailingRequiresClause = FD->getTrailingRequiresClause();
 
+// Note that FD->getType->getAs() can yield a
+// noexcept Expr which has been boiled down to a constant expression.
+// Going through the TypeSourceInfo obtains the actual expression which
+// will be traversed as part of the function -- unless we capture it
+// here and have TraverseStmt skip it.
+if (TypeSourceInfo *TSI = FD->getTypeSourceInfo()) {
+  FunctionProtoTypeLoc TL =
+  TSI->getTypeLoc().getAs();
+  if (const FunctionProtoType *FPT = TL.getTypePtr())
+NoexceptExpr = FPT->getNoexceptExpr();
+}
+  }
+
   // Do an AST traversal of the function/block body
   TraverseDecl(const_cast(CurrentCaller.CDecl));
 }
@@ -1269,7 +1283,8 @@ class Analyzer {
   // We skip the traversal of lambdas (beyond their captures, see
   // TraverseLambdaExpr below), so just caching this from our constructor
   // should suffice.
-  if (Statement != TrailingRequiresClause)
+  // The exact same is true for a conditional `noexcept()` clause.
+  if (Statement != TrailingRequiresClause && Statement != NoexceptExpr)
 return Base::TraverseStmt(Statement);
   return true;
 }
diff --git a/clang/test/Sema/attr-nonblocking-constraints.cpp 
b/clang/test/Sema/attr-nonblocking-constraints.cpp
index 19a4c3b7942b12..169c42ee35fe83 100644
--- a/clang/test/Sema/attr-nonblocking-constraints.cpp
+++ b/clang/test/Sema/attr-nonblocking-constraints.cpp
@@ -388,7 +388,7 @@ void nb26() [[clang::nonblocking]] {
abort_wrapper(); // no diagnostic
 }
 
-// --- Make sure we don't traverse a requires clause. ---
+// --- Make sure we don't traverse requires and noexcept clauses. ---
 
 // Apparently some requires clauses are able to be collapsed into a constant 
before the nonblocking
 // analysis sees any function calls. This example (extracted from a real-world 
case where
@@ -420,7 +420,9 @@ class expected {
   constexpr expected()
 {}
 
+  // This is a deliberate corruption of the real implementation for simplicity.
   constexpr expected(const expected&)
+noexcept(is_copy_constructible_v<_Tp> && is_copy_constructible_v<_Err>)
 requires(is_copy_constructible_v<_Tp> && is_copy_constructible_v<_Err>)
   = default;
 };
@@ -428,11 +430,17 @@ class expected {
 void test() [[clang::nonblocking]]
 {
expected a;
-   auto b = a;
+   auto b = a;// Copy constructor.
 }
 
 } // namespace ExpectedTest
 
+// Make sure that simple type traits don't cause violations.
+
+void nb27() [[clang::nonblocking]] {
+   bool x = __is_constructible(int, const int&);
+}
+
 // --- nonblocking implies noexcept ---
 #pragma clang diagnostic warning "-Wperf-constraint-implies-noexcept"
 

>From 02f1fc7974fffb97b9f23d7fb072f23bd9bdd89a Mon Sep 17 00:00:00 2001
From: Doug Wyatt 
Date: Fri, 8 Nov 2024 09:22:33 -0800
Subject: [PATCH 2/2] Review feedback: Check for no-prototype. Simplify test.

---
 clang/lib/Sema/SemaFunctionEffects.cpp   | 8 
 clang/test/Sema/attr-nonblocking-constraints.cpp | 8 +---
 2 files changed, 9 insertions(+), 7 deletions(-)

diff --git a/clang/lib/Sema/SemaFunctionEffects.cpp 
b/clang/lib/Sema/SemaFunctionEffects.cpp
index 70f6f9b6784cd8..a76a0a41276896 100644
--- a/clang/lib/Sema/SemaFunctionEffects.cpp
+++ b/clang/lib/Sema/SemaFunctionEffects.cpp
@@ -996,10 +996,10 @@ class Analyzer {
 // will be traversed as part of the function -- unless we capture it
 // here and have TraverseStmt skip it.
 if (TypeSourceInfo *TSI = FD->getTypeSourceIn

[clang] [Clang] SemaFunctionEffects: When verifying a function, ignore any conditional noexcept expression. (PR #115342)

2024-11-08 Thread Doug Wyatt via cfe-commits

dougsonos wrote:

> @dougsonos Oh also, I think I may have mentioned this before, but maybe you 
> might want to email Chris to obtain [commit 
> access](https://llvm.org/docs/DeveloperPolicy.html#obtaining-commit-access) 
> since you’re the main person maintaining all of the function effects code ;Þ

Thanks, I wrote to Chris.

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


[clang] [Clang] SemaFunctionEffects: When verifying a function, ignore any conditional noexcept expression. (PR #115342)

2024-11-08 Thread Doug Wyatt via cfe-commits

https://github.com/dougsonos updated 
https://github.com/llvm/llvm-project/pull/115342

>From a0b6093fcf24ade7ce9ee0c3d65f679a41751225 Mon Sep 17 00:00:00 2001
From: Doug Wyatt 
Date: Tue, 5 Nov 2024 13:35:50 -0800
Subject: [PATCH 1/2] [Clang] SemaFunctionEffects: When verifying a function,
 ignore any conditional noexcept expression.

---
 clang/lib/Sema/SemaFunctionEffects.cpp| 19 +--
 .../Sema/attr-nonblocking-constraints.cpp | 12 ++--
 2 files changed, 27 insertions(+), 4 deletions(-)

diff --git a/clang/lib/Sema/SemaFunctionEffects.cpp 
b/clang/lib/Sema/SemaFunctionEffects.cpp
index ab728f24d8a271..70f6f9b6784cd8 100644
--- a/clang/lib/Sema/SemaFunctionEffects.cpp
+++ b/clang/lib/Sema/SemaFunctionEffects.cpp
@@ -972,6 +972,7 @@ class Analyzer {
 CallableInfo &CurrentCaller;
 ViolationSite VSite;
 const Expr *TrailingRequiresClause = nullptr;
+const Expr *NoexceptExpr = nullptr;
 
 FunctionBodyASTVisitor(Analyzer &Outer,
PendingFunctionAnalysis &CurrentFunction,
@@ -986,9 +987,22 @@ class Analyzer {
   if (auto *Dtor = dyn_cast(CurrentCaller.CDecl))
 followDestructor(dyn_cast(Dtor->getParent()), Dtor);
 
-  if (auto *FD = dyn_cast(CurrentCaller.CDecl))
+  if (auto *FD = dyn_cast(CurrentCaller.CDecl)) {
 TrailingRequiresClause = FD->getTrailingRequiresClause();
 
+// Note that FD->getType->getAs() can yield a
+// noexcept Expr which has been boiled down to a constant expression.
+// Going through the TypeSourceInfo obtains the actual expression which
+// will be traversed as part of the function -- unless we capture it
+// here and have TraverseStmt skip it.
+if (TypeSourceInfo *TSI = FD->getTypeSourceInfo()) {
+  FunctionProtoTypeLoc TL =
+  TSI->getTypeLoc().getAs();
+  if (const FunctionProtoType *FPT = TL.getTypePtr())
+NoexceptExpr = FPT->getNoexceptExpr();
+}
+  }
+
   // Do an AST traversal of the function/block body
   TraverseDecl(const_cast(CurrentCaller.CDecl));
 }
@@ -1269,7 +1283,8 @@ class Analyzer {
   // We skip the traversal of lambdas (beyond their captures, see
   // TraverseLambdaExpr below), so just caching this from our constructor
   // should suffice.
-  if (Statement != TrailingRequiresClause)
+  // The exact same is true for a conditional `noexcept()` clause.
+  if (Statement != TrailingRequiresClause && Statement != NoexceptExpr)
 return Base::TraverseStmt(Statement);
   return true;
 }
diff --git a/clang/test/Sema/attr-nonblocking-constraints.cpp 
b/clang/test/Sema/attr-nonblocking-constraints.cpp
index 19a4c3b7942b12..169c42ee35fe83 100644
--- a/clang/test/Sema/attr-nonblocking-constraints.cpp
+++ b/clang/test/Sema/attr-nonblocking-constraints.cpp
@@ -388,7 +388,7 @@ void nb26() [[clang::nonblocking]] {
abort_wrapper(); // no diagnostic
 }
 
-// --- Make sure we don't traverse a requires clause. ---
+// --- Make sure we don't traverse requires and noexcept clauses. ---
 
 // Apparently some requires clauses are able to be collapsed into a constant 
before the nonblocking
 // analysis sees any function calls. This example (extracted from a real-world 
case where
@@ -420,7 +420,9 @@ class expected {
   constexpr expected()
 {}
 
+  // This is a deliberate corruption of the real implementation for simplicity.
   constexpr expected(const expected&)
+noexcept(is_copy_constructible_v<_Tp> && is_copy_constructible_v<_Err>)
 requires(is_copy_constructible_v<_Tp> && is_copy_constructible_v<_Err>)
   = default;
 };
@@ -428,11 +430,17 @@ class expected {
 void test() [[clang::nonblocking]]
 {
expected a;
-   auto b = a;
+   auto b = a;// Copy constructor.
 }
 
 } // namespace ExpectedTest
 
+// Make sure that simple type traits don't cause violations.
+
+void nb27() [[clang::nonblocking]] {
+   bool x = __is_constructible(int, const int&);
+}
+
 // --- nonblocking implies noexcept ---
 #pragma clang diagnostic warning "-Wperf-constraint-implies-noexcept"
 

>From 02f1fc7974fffb97b9f23d7fb072f23bd9bdd89a Mon Sep 17 00:00:00 2001
From: Doug Wyatt 
Date: Fri, 8 Nov 2024 09:22:33 -0800
Subject: [PATCH 2/2] Review feedback: Check for no-prototype. Simplify test.

---
 clang/lib/Sema/SemaFunctionEffects.cpp   | 8 
 clang/test/Sema/attr-nonblocking-constraints.cpp | 8 +---
 2 files changed, 9 insertions(+), 7 deletions(-)

diff --git a/clang/lib/Sema/SemaFunctionEffects.cpp 
b/clang/lib/Sema/SemaFunctionEffects.cpp
index 70f6f9b6784cd8..a76a0a41276896 100644
--- a/clang/lib/Sema/SemaFunctionEffects.cpp
+++ b/clang/lib/Sema/SemaFunctionEffects.cpp
@@ -996,10 +996,10 @@ class Analyzer {
 // will be traversed as part of the function -- unless we capture it
 // here and have TraverseStmt skip it.
 if (TypeSourceInfo *TSI = FD->getTypeSourceIn

[clang] [Clang] SemaFunctionEffects: When verifying a function, ignore any conditional noexcept expression. (PR #115342)

2024-11-08 Thread Doug Wyatt via cfe-commits


@@ -986,9 +987,22 @@ class Analyzer {
   if (auto *Dtor = dyn_cast(CurrentCaller.CDecl))
 followDestructor(dyn_cast(Dtor->getParent()), Dtor);
 
-  if (auto *FD = dyn_cast(CurrentCaller.CDecl))
+  if (auto *FD = dyn_cast(CurrentCaller.CDecl)) {
 TrailingRequiresClause = FD->getTrailingRequiresClause();
 
+// Note that FD->getType->getAs() can yield a
+// noexcept Expr which has been boiled down to a constant expression.
+// Going through the TypeSourceInfo obtains the actual expression which
+// will be traversed as part of the function -- unless we capture it
+// here and have TraverseStmt skip it.
+if (TypeSourceInfo *TSI = FD->getTypeSourceInfo()) {
+  FunctionProtoTypeLoc TL =
+  TSI->getTypeLoc().getAs();
+  if (const FunctionProtoType *FPT = TL.getTypePtr())

dougsonos wrote:

Yup indeed the C no-prototype test was crashing :/

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


[clang] [Clang] SemaFunctionEffects: When verifying a function, ignore any conditional noexcept expression. (PR #115342)

2024-11-08 Thread Doug Wyatt via cfe-commits


@@ -986,9 +987,22 @@ class Analyzer {
   if (auto *Dtor = dyn_cast(CurrentCaller.CDecl))
 followDestructor(dyn_cast(Dtor->getParent()), Dtor);
 
-  if (auto *FD = dyn_cast(CurrentCaller.CDecl))
+  if (auto *FD = dyn_cast(CurrentCaller.CDecl)) {
 TrailingRequiresClause = FD->getTrailingRequiresClause();
 
+// Note that FD->getType->getAs() can yield a
+// noexcept Expr which has been boiled down to a constant expression.
+// Going through the TypeSourceInfo obtains the actual expression which
+// will be traversed as part of the function -- unless we capture it
+// here and have TraverseStmt skip it.
+if (TypeSourceInfo *TSI = FD->getTypeSourceInfo()) {
+  FunctionProtoTypeLoc TL =
+  TSI->getTypeLoc().getAs();
+  if (const FunctionProtoType *FPT = TL.getTypePtr())
+NoexceptExpr = FPT->getNoexceptExpr();
+}
+  }
+
   // Do an AST traversal of the function/block body
   TraverseDecl(const_cast(CurrentCaller.CDecl));

dougsonos wrote:

That's a good thought. Looking at `TraverseFunctionHelper`:
- the template arguments should be ignorable.
- need to traverse the function's type because it contains the parameters -- 
e.g. I caught someone passing a vector by value instead of by reference and 
that showed up first as a call to the vector's destructor, located in the 
parameter list.
- but the function's type is where the noexcept expression comes from.
- can skip the trailing return clause.
- need to traverse the constructor initializers.
- need to traverse the body of course.

I'm not excited to tear this apart at the moment but maybe the next bug that 
comes up in this area can drive an improvement.

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


[clang] [Clang] SemaFunctionEffects: When verifying a function, ignore any conditional noexcept expression. (PR #115342)

2024-11-07 Thread Doug Wyatt via cfe-commits

https://github.com/dougsonos created 
https://github.com/llvm/llvm-project/pull/115342

Would have been part of my last PR (#142666) if I'd found it a few hours sooner.

>From a0b6093fcf24ade7ce9ee0c3d65f679a41751225 Mon Sep 17 00:00:00 2001
From: Doug Wyatt 
Date: Tue, 5 Nov 2024 13:35:50 -0800
Subject: [PATCH] [Clang] SemaFunctionEffects: When verifying a function,
 ignore any conditional noexcept expression.

---
 clang/lib/Sema/SemaFunctionEffects.cpp| 19 +--
 .../Sema/attr-nonblocking-constraints.cpp | 12 ++--
 2 files changed, 27 insertions(+), 4 deletions(-)

diff --git a/clang/lib/Sema/SemaFunctionEffects.cpp 
b/clang/lib/Sema/SemaFunctionEffects.cpp
index ab728f24d8a271..70f6f9b6784cd8 100644
--- a/clang/lib/Sema/SemaFunctionEffects.cpp
+++ b/clang/lib/Sema/SemaFunctionEffects.cpp
@@ -972,6 +972,7 @@ class Analyzer {
 CallableInfo &CurrentCaller;
 ViolationSite VSite;
 const Expr *TrailingRequiresClause = nullptr;
+const Expr *NoexceptExpr = nullptr;
 
 FunctionBodyASTVisitor(Analyzer &Outer,
PendingFunctionAnalysis &CurrentFunction,
@@ -986,9 +987,22 @@ class Analyzer {
   if (auto *Dtor = dyn_cast(CurrentCaller.CDecl))
 followDestructor(dyn_cast(Dtor->getParent()), Dtor);
 
-  if (auto *FD = dyn_cast(CurrentCaller.CDecl))
+  if (auto *FD = dyn_cast(CurrentCaller.CDecl)) {
 TrailingRequiresClause = FD->getTrailingRequiresClause();
 
+// Note that FD->getType->getAs() can yield a
+// noexcept Expr which has been boiled down to a constant expression.
+// Going through the TypeSourceInfo obtains the actual expression which
+// will be traversed as part of the function -- unless we capture it
+// here and have TraverseStmt skip it.
+if (TypeSourceInfo *TSI = FD->getTypeSourceInfo()) {
+  FunctionProtoTypeLoc TL =
+  TSI->getTypeLoc().getAs();
+  if (const FunctionProtoType *FPT = TL.getTypePtr())
+NoexceptExpr = FPT->getNoexceptExpr();
+}
+  }
+
   // Do an AST traversal of the function/block body
   TraverseDecl(const_cast(CurrentCaller.CDecl));
 }
@@ -1269,7 +1283,8 @@ class Analyzer {
   // We skip the traversal of lambdas (beyond their captures, see
   // TraverseLambdaExpr below), so just caching this from our constructor
   // should suffice.
-  if (Statement != TrailingRequiresClause)
+  // The exact same is true for a conditional `noexcept()` clause.
+  if (Statement != TrailingRequiresClause && Statement != NoexceptExpr)
 return Base::TraverseStmt(Statement);
   return true;
 }
diff --git a/clang/test/Sema/attr-nonblocking-constraints.cpp 
b/clang/test/Sema/attr-nonblocking-constraints.cpp
index 19a4c3b7942b12..169c42ee35fe83 100644
--- a/clang/test/Sema/attr-nonblocking-constraints.cpp
+++ b/clang/test/Sema/attr-nonblocking-constraints.cpp
@@ -388,7 +388,7 @@ void nb26() [[clang::nonblocking]] {
abort_wrapper(); // no diagnostic
 }
 
-// --- Make sure we don't traverse a requires clause. ---
+// --- Make sure we don't traverse requires and noexcept clauses. ---
 
 // Apparently some requires clauses are able to be collapsed into a constant 
before the nonblocking
 // analysis sees any function calls. This example (extracted from a real-world 
case where
@@ -420,7 +420,9 @@ class expected {
   constexpr expected()
 {}
 
+  // This is a deliberate corruption of the real implementation for simplicity.
   constexpr expected(const expected&)
+noexcept(is_copy_constructible_v<_Tp> && is_copy_constructible_v<_Err>)
 requires(is_copy_constructible_v<_Tp> && is_copy_constructible_v<_Err>)
   = default;
 };
@@ -428,11 +430,17 @@ class expected {
 void test() [[clang::nonblocking]]
 {
expected a;
-   auto b = a;
+   auto b = a;// Copy constructor.
 }
 
 } // namespace ExpectedTest
 
+// Make sure that simple type traits don't cause violations.
+
+void nb27() [[clang::nonblocking]] {
+   bool x = __is_constructible(int, const int&);
+}
+
 // --- nonblocking implies noexcept ---
 #pragma clang diagnostic warning "-Wperf-constraint-implies-noexcept"
 

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [clang] SemaFunctionEffects: When verifying a function, ignore any trailing 'requires' clause. (PR #114266)

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

dougsonos wrote:

> > is a builtin function `__is_constructible()`
> 
> Technically a type trait not a function—those are different because they take 
> _types_ as arguments, and there are separate expressions to represent them 
> (`UnaryExprOrTypeTraitExpr`, `TypeTraitExpr`, and a few more); maybe we’re 
> somehow not accounting for those?
> 
> Do we have tests for effect analysis that use builtin type traits?

Thanks, I didn't know that these were type traits, they looked like special 
builtin functions to me, and that ignorance may be reflected in some of the 
diagnostics.

Generally expressions are ignored except `CallExpr` IIRC.

I'll look into this further. Thanks for the review here.

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


[clang] [clang] SemaFunctionEffects: When verifying a function, ignore any trailing 'requires' clause. (PR #114266)

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

https://github.com/dougsonos updated 
https://github.com/llvm/llvm-project/pull/114266

>From 6a8a3f21eb23b8b7d63bd8b0fb6e2e85ff1951df Mon Sep 17 00:00:00 2001
From: Doug Wyatt 
Date: Wed, 30 Oct 2024 09:53:58 -0700
Subject: [PATCH 1/3] [clang] SemaFunctionEffects: When verifying a function,
 ignore any trailing 'requires' clause.

---
 clang/lib/Sema/SemaFunctionEffects.cpp | 10 ++
 1 file changed, 10 insertions(+)

diff --git a/clang/lib/Sema/SemaFunctionEffects.cpp 
b/clang/lib/Sema/SemaFunctionEffects.cpp
index 3fa326db06ee41..f7ff8b92d8a929 100644
--- a/clang/lib/Sema/SemaFunctionEffects.cpp
+++ b/clang/lib/Sema/SemaFunctionEffects.cpp
@@ -971,6 +971,7 @@ class Analyzer {
 PendingFunctionAnalysis &CurrentFunction;
 CallableInfo &CurrentCaller;
 ViolationSite VSite;
+const Expr *TrailingRequiresClause = nullptr;
 
 FunctionBodyASTVisitor(Analyzer &Outer,
PendingFunctionAnalysis &CurrentFunction,
@@ -985,6 +986,9 @@ class Analyzer {
   if (auto *Dtor = dyn_cast(CurrentCaller.CDecl))
 followDestructor(dyn_cast(Dtor->getParent()), Dtor);
 
+  if (auto *FD = dyn_cast(CurrentCaller.CDecl))
+TrailingRequiresClause = FD->getTrailingRequiresClause();
+
   // Do an AST traversal of the function/block body
   TraverseDecl(const_cast(CurrentCaller.CDecl));
 }
@@ -1259,6 +1263,12 @@ class Analyzer {
   return true;
 }
 
+bool TraverseStmt(Stmt *Statement) {
+  if (Statement != TrailingRequiresClause)
+return Base::TraverseStmt(Statement);
+  return true;
+}
+
 bool TraverseConstructorInitializer(CXXCtorInitializer *Init) {
   ViolationSite PrevVS = VSite;
   if (Init->isAnyMemberInitializer())

>From 9de0cf0c514c9dc3bc0b282bab6ec52d3c8156b4 Mon Sep 17 00:00:00 2001
From: Doug Wyatt 
Date: Sat, 2 Nov 2024 09:03:48 -0700
Subject: [PATCH 2/3] Add a test.

---
 .../Sema/attr-nonblocking-constraints.cpp | 45 +++
 1 file changed, 45 insertions(+)

diff --git a/clang/test/Sema/attr-nonblocking-constraints.cpp 
b/clang/test/Sema/attr-nonblocking-constraints.cpp
index f23093d4dc8a96..19a4c3b7942b12 100644
--- a/clang/test/Sema/attr-nonblocking-constraints.cpp
+++ b/clang/test/Sema/attr-nonblocking-constraints.cpp
@@ -388,6 +388,51 @@ void nb26() [[clang::nonblocking]] {
abort_wrapper(); // no diagnostic
 }
 
+// --- Make sure we don't traverse a requires clause. ---
+
+// Apparently some requires clauses are able to be collapsed into a constant 
before the nonblocking
+// analysis sees any function calls. This example (extracted from a real-world 
case where
+// `operator&&` in , preceding the inclusion of ) is 
sufficiently complex
+// to look like it contains function calls. There may be simpler examples.
+
+namespace ExpectedTest {
+
+template 
+inline constexpr bool is_copy_constructible_v = __is_constructible(_Tp, _Tp&);
+
+template 
+struct enable_if {};
+template 
+struct enable_if {
+  typedef _Tp type;
+};
+
+template 
+using enable_if_t = typename enable_if<_Bp, _Tp>::type;
+
+// Doesn't seem to matter whether the enable_if is true or false.
+template > = 0>
+inline bool operator&&(const E1& x, const E2& y);
+
+template 
+class expected {
+public:
+  constexpr expected()
+{}
+
+  constexpr expected(const expected&)
+requires(is_copy_constructible_v<_Tp> && is_copy_constructible_v<_Err>)
+  = default;
+};
+
+void test() [[clang::nonblocking]]
+{
+   expected a;
+   auto b = a;
+}
+
+} // namespace ExpectedTest
+
 // --- nonblocking implies noexcept ---
 #pragma clang diagnostic warning "-Wperf-constraint-implies-noexcept"
 

>From ca803cd3a8dba0045e2b7f7c3a44e358631f1c45 Mon Sep 17 00:00:00 2001
From: Doug Wyatt 
Date: Mon, 4 Nov 2024 07:55:14 -0800
Subject: [PATCH 3/3] From review: Add comments.

---
 clang/lib/Sema/SemaFunctionEffects.cpp | 6 ++
 1 file changed, 6 insertions(+)

diff --git a/clang/lib/Sema/SemaFunctionEffects.cpp 
b/clang/lib/Sema/SemaFunctionEffects.cpp
index f7ff8b92d8a929..ab728f24d8a271 100644
--- a/clang/lib/Sema/SemaFunctionEffects.cpp
+++ b/clang/lib/Sema/SemaFunctionEffects.cpp
@@ -1264,6 +1264,11 @@ class Analyzer {
 }
 
 bool TraverseStmt(Stmt *Statement) {
+  // If this statement is a `requires` clause from the top-level function
+  // being traversed, ignore it, since it's not generating runtime code.
+  // We skip the traversal of lambdas (beyond their captures, see
+  // TraverseLambdaExpr below), so just caching this from our constructor
+  // should suffice.
   if (Statement != TrailingRequiresClause)
 return Base::TraverseStmt(Statement);
   return true;
@@ -1307,6 +1312,7 @@ class Analyzer {
 }
 
 bool TraverseBlockExpr(BlockExpr * /*unused*/) {
+  // As with lambdas, don't traverse the block's body.
   // TODO: are the capture expressions (ctor call?) safe?
   return true;
 }

___
cfe-

[clang] [clang] SemaFunctionEffects: When verifying a function, ignore any trailing 'requires' clause. (PR #114266)

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

https://github.com/dougsonos edited 
https://github.com/llvm/llvm-project/pull/114266
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [clang] SemaFunctionEffects: When verifying a function, ignore any trailing 'requires' clause. (PR #114266)

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


@@ -985,6 +986,9 @@ class Analyzer {
   if (auto *Dtor = dyn_cast(CurrentCaller.CDecl))
 followDestructor(dyn_cast(Dtor->getParent()), Dtor);
 
+  if (auto *FD = dyn_cast(CurrentCaller.CDecl))
+TrailingRequiresClause = FD->getTrailingRequiresClause();

dougsonos wrote:

That's a good question. This is sufficient/correct for a couple of reasons:

- This whole `RecursiveASTVisitor` subclass is called for one 
function/lambda/block at a time, and this (new) member `TrailingRequiresClause` 
is being set for that "top-level" function only.
- Lambdas and blocks have overridden `Traverse` methods in order to skip their 
bodies -- which also mean we don't care about, and don't visit, their 
`requires` clauses.

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


[clang] [clang] SemaFunctionEffects: When verifying a function, ignore any trailing 'requires' clause. (PR #114266)

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

https://github.com/dougsonos deleted 
https://github.com/llvm/llvm-project/pull/114266
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [clang] SemaFunctionEffects: When verifying a function, ignore any trailing 'requires' clause. (PR #114266)

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


@@ -985,6 +986,9 @@ class Analyzer {
   if (auto *Dtor = dyn_cast(CurrentCaller.CDecl))
 followDestructor(dyn_cast(Dtor->getParent()), Dtor);
 
+  if (auto *FD = dyn_cast(CurrentCaller.CDecl))
+TrailingRequiresClause = FD->getTrailingRequiresClause();

dougsonos wrote:

Looking at an AST dump from:

```c++
template 
struct integral_constant {
  static constexpr const _Tp value = __v;
  typedef _Tp value_type;
  typedef integral_constant type;
  constexpr operator value_type() const noexcept { return value; }
  constexpr value_type operator()() const noexcept { return value; }
};

template 
constexpr const _Tp integral_constant<_Tp, __v>::value;

typedef integral_constant true_type;
typedef integral_constant false_type;

template 
struct is_default_constructible : public integral_constant {};

template 
inline constexpr bool is_default_constructible_v = __is_constructible(_Tp);

template 
void f()
requires is_default_constructible_v
{
auto g = [](int x) requires is_default_constructible_v {

};
}
```

There are separate `UnresolvedLookupExpr`'s attached to the `FunctionDecl` for 
`f` and to the lambda-generated class's `operator()` and `CXXConversionDecl`.

I found `getTrailingReturnClause()` being used by 
`RecursiveASTVisitor::TraverseFunctionHelper()`.

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


[clang] [clang] SemaFunctionEffects: When verifying a function, ignore any trailing 'requires' clause. (PR #114266)

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

dougsonos wrote:

I was able to (manually) extract a reduction from libc++ and write a test that 
exposed the issue / verified that it's fixed. It's also here: 
https://godbolt.org/z/9absooo6G

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


[clang] [clang] SemaFunctionEffects: When verifying a function, ignore any trailing 'requires' clause. (PR #114266)

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

https://github.com/dougsonos updated 
https://github.com/llvm/llvm-project/pull/114266

>From 6a8a3f21eb23b8b7d63bd8b0fb6e2e85ff1951df Mon Sep 17 00:00:00 2001
From: Doug Wyatt 
Date: Wed, 30 Oct 2024 09:53:58 -0700
Subject: [PATCH 1/2] [clang] SemaFunctionEffects: When verifying a function,
 ignore any trailing 'requires' clause.

---
 clang/lib/Sema/SemaFunctionEffects.cpp | 10 ++
 1 file changed, 10 insertions(+)

diff --git a/clang/lib/Sema/SemaFunctionEffects.cpp 
b/clang/lib/Sema/SemaFunctionEffects.cpp
index 3fa326db06ee41..f7ff8b92d8a929 100644
--- a/clang/lib/Sema/SemaFunctionEffects.cpp
+++ b/clang/lib/Sema/SemaFunctionEffects.cpp
@@ -971,6 +971,7 @@ class Analyzer {
 PendingFunctionAnalysis &CurrentFunction;
 CallableInfo &CurrentCaller;
 ViolationSite VSite;
+const Expr *TrailingRequiresClause = nullptr;
 
 FunctionBodyASTVisitor(Analyzer &Outer,
PendingFunctionAnalysis &CurrentFunction,
@@ -985,6 +986,9 @@ class Analyzer {
   if (auto *Dtor = dyn_cast(CurrentCaller.CDecl))
 followDestructor(dyn_cast(Dtor->getParent()), Dtor);
 
+  if (auto *FD = dyn_cast(CurrentCaller.CDecl))
+TrailingRequiresClause = FD->getTrailingRequiresClause();
+
   // Do an AST traversal of the function/block body
   TraverseDecl(const_cast(CurrentCaller.CDecl));
 }
@@ -1259,6 +1263,12 @@ class Analyzer {
   return true;
 }
 
+bool TraverseStmt(Stmt *Statement) {
+  if (Statement != TrailingRequiresClause)
+return Base::TraverseStmt(Statement);
+  return true;
+}
+
 bool TraverseConstructorInitializer(CXXCtorInitializer *Init) {
   ViolationSite PrevVS = VSite;
   if (Init->isAnyMemberInitializer())

>From 9de0cf0c514c9dc3bc0b282bab6ec52d3c8156b4 Mon Sep 17 00:00:00 2001
From: Doug Wyatt 
Date: Sat, 2 Nov 2024 09:03:48 -0700
Subject: [PATCH 2/2] Add a test.

---
 .../Sema/attr-nonblocking-constraints.cpp | 45 +++
 1 file changed, 45 insertions(+)

diff --git a/clang/test/Sema/attr-nonblocking-constraints.cpp 
b/clang/test/Sema/attr-nonblocking-constraints.cpp
index f23093d4dc8a96..19a4c3b7942b12 100644
--- a/clang/test/Sema/attr-nonblocking-constraints.cpp
+++ b/clang/test/Sema/attr-nonblocking-constraints.cpp
@@ -388,6 +388,51 @@ void nb26() [[clang::nonblocking]] {
abort_wrapper(); // no diagnostic
 }
 
+// --- Make sure we don't traverse a requires clause. ---
+
+// Apparently some requires clauses are able to be collapsed into a constant 
before the nonblocking
+// analysis sees any function calls. This example (extracted from a real-world 
case where
+// `operator&&` in , preceding the inclusion of ) is 
sufficiently complex
+// to look like it contains function calls. There may be simpler examples.
+
+namespace ExpectedTest {
+
+template 
+inline constexpr bool is_copy_constructible_v = __is_constructible(_Tp, _Tp&);
+
+template 
+struct enable_if {};
+template 
+struct enable_if {
+  typedef _Tp type;
+};
+
+template 
+using enable_if_t = typename enable_if<_Bp, _Tp>::type;
+
+// Doesn't seem to matter whether the enable_if is true or false.
+template > = 0>
+inline bool operator&&(const E1& x, const E2& y);
+
+template 
+class expected {
+public:
+  constexpr expected()
+{}
+
+  constexpr expected(const expected&)
+requires(is_copy_constructible_v<_Tp> && is_copy_constructible_v<_Err>)
+  = default;
+};
+
+void test() [[clang::nonblocking]]
+{
+   expected a;
+   auto b = a;
+}
+
+} // namespace ExpectedTest
+
 // --- nonblocking implies noexcept ---
 #pragma clang diagnostic warning "-Wperf-constraint-implies-noexcept"
 

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [clang] SemaFunctionEffects: When verifying a function, ignore any trailing 'requires' clause. (PR #114266)

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

dougsonos wrote:

I spent a bit more time trying to extract a reduction from libc++ today but 
failed. Here are the diagnostics that (fortunately) were enough to devise the 
fix in this PR:

```c++
./nonblocking-wip.cpp:54:30: warning: function with 'nonblocking' attribute 
must not call non-'nonblocking' constructor 'std::expected::expected' 
[-Wfunction-effects]
   54 |  std::expected d = c;
  |  ^
./usr/include/c++/v1/__expected/expected.h:483:14: note: function pointer 
cannot be inferred 'nonblocking'
  483 | requires(is_copy_constructible_v<_Tp> && 
is_copy_constructible_v<_Err> && is_trivially_copy_constructible_v<_Tp> &&
  |  ^
.//nonblocking-wip.cpp:54:30: note: in template expansion here
   54 |  std::expected d = c;
  |  ^
```

That showed me that the `requires` clause is being traversed (inappropriately) 
and doing something that looks like an indirect call (i.e. through a function 
pointer).

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


[clang] [clang] SemaFunctionEffects: When verifying a function, ignore any trailing 'requires' clause. (PR #114266)

2024-10-31 Thread Doug Wyatt via cfe-commits

dougsonos wrote:

> Also, @dougsonos, can you make a godbolt link for this? I’m trying to 
> reproduce it, but compiling the code you provided doesn’t result in any 
> diagnostics for me, but I might be missing a flag.

Yeah, I can't repro in godbolt either. This shows that all the effect analysis 
is in the clang-20 trunk on godbolt though:
https://godbolt.org/z/rP1E5ebhG

That makes me think that the issue may be specific to the um, older, version of 
libc++ that I've been testing against.

I'm pretty sure it has something to do with type traits in the requires() 
clause of the std::expected copy constructor, interacting badly with a global 
`operator&&` in ``.

What I found impossible about creduce is that at some point it started ignoring 
requirements like the `requires` clause and collapsing all the templates.

Thanks for taking a look.

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


[clang] [rtsan][NFC] Add documentation link to Function Effects (PR #113979)

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

dougsonos wrote:

LGTM

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


[clang] [clang] SemaFunctionEffects: When verifying a function, ignore any trailing 'requires' clause. (PR #114266)

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

https://github.com/dougsonos created 
https://github.com/llvm/llvm-project/pull/114266

Clearly there's an omission here, that a trailing `requires` clause in a called 
function is being subject to effect analysis.

But despite many hours of effort I haven't been able to create a self-contained 
reproducer. My best effort:

```c++
#include 
#include 
#include 

template 
struct integral_constant {
  static constexpr _Tp value = __v;
  typedef _Tp value_type;
  typedef integral_constant type;
  constexpr operator value_type() const noexcept { return value; }
  constexpr value_type operator()() const noexcept { return value; }
};

template 
struct IsInt : public integral_constant {};

template <>
struct IsInt : public integral_constant {};

template 
inline constexpr bool IsInt_V = IsInt::value;

template 
struct ExpectedLike {
ExpectedLike() = default;

constexpr ExpectedLike(const ExpectedLike&)
//  requires(IsInt_V && IsInt_V && IsInt_V)
requires(std::is_copy_constructible_v && std::is_copy_constructible_v 
&& std::is_trivially_copy_constructible_v &&
 std::is_trivially_copy_constructible_v)

= default;

};

void nb_xx() [[clang::nonblocking]]
{
ExpectedLike a;
auto b = a;
// No warning. I don't know why.

std::expected c;
std::expected d = c;
// ^^ warning: function with 'nonblocking' attribute must not call 
non-'nonblocking' constructor 'std::expected::expected' 
[-Wfunction-effects]
}
```

The two elements of the mystery are:

- why doesn't my `ExpectedLike` copy constructor reproduce the behavior 
of `std::expected`?
- why does reproduction with `std::expected` depend on the global `operator &&` 
in ``?

I've verified that the change to SemaFunctionEffects fixes the issue, but I'd 
sure like to be able to construct a self-contained test.

>From 6a8a3f21eb23b8b7d63bd8b0fb6e2e85ff1951df Mon Sep 17 00:00:00 2001
From: Doug Wyatt 
Date: Wed, 30 Oct 2024 09:53:58 -0700
Subject: [PATCH] [clang] SemaFunctionEffects: When verifying a function,
 ignore any trailing 'requires' clause.

---
 clang/lib/Sema/SemaFunctionEffects.cpp | 10 ++
 1 file changed, 10 insertions(+)

diff --git a/clang/lib/Sema/SemaFunctionEffects.cpp 
b/clang/lib/Sema/SemaFunctionEffects.cpp
index 3fa326db06ee41..f7ff8b92d8a929 100644
--- a/clang/lib/Sema/SemaFunctionEffects.cpp
+++ b/clang/lib/Sema/SemaFunctionEffects.cpp
@@ -971,6 +971,7 @@ class Analyzer {
 PendingFunctionAnalysis &CurrentFunction;
 CallableInfo &CurrentCaller;
 ViolationSite VSite;
+const Expr *TrailingRequiresClause = nullptr;
 
 FunctionBodyASTVisitor(Analyzer &Outer,
PendingFunctionAnalysis &CurrentFunction,
@@ -985,6 +986,9 @@ class Analyzer {
   if (auto *Dtor = dyn_cast(CurrentCaller.CDecl))
 followDestructor(dyn_cast(Dtor->getParent()), Dtor);
 
+  if (auto *FD = dyn_cast(CurrentCaller.CDecl))
+TrailingRequiresClause = FD->getTrailingRequiresClause();
+
   // Do an AST traversal of the function/block body
   TraverseDecl(const_cast(CurrentCaller.CDecl));
 }
@@ -1259,6 +1263,12 @@ class Analyzer {
   return true;
 }
 
+bool TraverseStmt(Stmt *Statement) {
+  if (Statement != TrailingRequiresClause)
+return Base::TraverseStmt(Statement);
+  return true;
+}
+
 bool TraverseConstructorInitializer(CXXCtorInitializer *Init) {
   ViolationSite PrevVS = VSite;
   if (Init->isAnyMemberInitializer())

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] Add clang/docs/FunctionEffectAnalysis.rst. (PR #109855)

2024-10-28 Thread Doug Wyatt via cfe-commits

dougsonos wrote:

@cjappl please merge at will. Thank you!

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


[clang] Add clang/docs/FunctionEffectAnalysis.rst. (PR #109855)

2024-10-28 Thread Doug Wyatt via cfe-commits

https://github.com/dougsonos updated 
https://github.com/llvm/llvm-project/pull/109855

>From 085965b324efde41168c5d51db3a368578d3458f Mon Sep 17 00:00:00 2001
From: Doug Wyatt 
Date: Mon, 23 Sep 2024 14:44:32 -0700
Subject: [PATCH 1/7] Add clang/docs/FunctionEffectAnalysis.rst.

---
 clang/docs/FunctionEffectAnalysis.rst | 503 ++
 clang/docs/index.rst  |   1 +
 2 files changed, 504 insertions(+)
 create mode 100644 clang/docs/FunctionEffectAnalysis.rst

diff --git a/clang/docs/FunctionEffectAnalysis.rst 
b/clang/docs/FunctionEffectAnalysis.rst
new file mode 100644
index 00..8c1cf8a483c5f1
--- /dev/null
+++ b/clang/docs/FunctionEffectAnalysis.rst
@@ -0,0 +1,503 @@
+
+Function Effect Analysis
+
+
+Introduction
+
+
+Clang Function Effect Analysis is a C++ language extension which can warn 
about "unsafe"
+constructs. The feature is currently tailored for the Performance Constraint 
attributes,
+``nonblocking`` and ``nonallocating``; functions with these attributes are 
verified as not
+containing any language constructs or calls to other functions which violate 
the constraint.
+(See :doc:`AttributeReference`.)
+
+
+The ``nonblocking`` and ``nonallocating`` attributes
+
+
+Attribute syntax
+
+
+The ``nonblocking`` and ``nonallocating`` attributes apply to function types, 
allowing them to be
+attached to functions, blocks, function pointers, lambdas, and member 
functions.
+
+.. code-block:: c++
+
+  // Functions
+  void nonblockingFunction() [[clang::nonblocking]];
+  void nonallocatingFunction() [[clang::nonallocating]];
+
+  // Function pointers
+  void (*nonblockingFunctionPtr)() [[clang::nonblocking]];
+
+  // Typedefs, type aliases.
+  typedef void (*NBFunctionPtrTypedef)() [[clang::nonblocking]];
+  using NBFunctionPtrTypeAlias_gnu = __attribute__((nonblocking)) void (*)();
+  using NBFunctionPtrTypeAlias_std = void (*)() [[clang::nonblocking]];
+
+  // C++ methods
+  struct Struct {
+void NBMethod() [[clang::nonblocking]];
+  };
+
+  // C++ lambdas
+  auto nbLambda = []() [[clang::nonblocking]] {};
+
+  // Blocks
+  void (^nbBlock)() = ^() [[clang::nonblocking]] {};
+
+The attribute applies only to the function itself. In particular, it does not 
apply to any nested
+functions or declarations, such as blocks, lambdas, and local classes.
+
+This document uses the C++/C23 syntax ``[[clang::nonblocking]]``, since it 
parallels the placement 
+of the ``noexcept`` specifier, and the attributes have other similarities to 
``noexcept``. The GNU
+``__attribute__((nonblocking))`` syntax is also supported. Note that it 
requires a different 
+placement on a C++ type alias.
+
+Like ``noexcept``, ``nonblocking`` and ``nonallocating`` have an optional 
argument, a compile-time
+constant boolean expression. By default, the argument is true, so 
``[[clang::nonblocking(true)]]``
+is equivalent to ``[[clang::nonblocking]]``, and declares the function type as 
never locking.
+
+
+Attribute semantics
+---
+
+Together with ``noexcept``, the ``nonallocating`` and ``nonblocking`` 
attributes define an ordered
+series of performance constraints. From weakest to strongest:
+
+- ``noexcept`` (as per the C++ standard): The function type will never throw 
an exception.
+- ``nonallocating``: The function type will never allocate memory on the heap, 
and never throw an
+  exception.
+- ``nonblocking``: The function type will never block on a lock, never 
allocate memory on the heap,
+  and never throw an exception.
+
+``nonblocking`` includes the ``nonallocating`` guarantee. 
+
+``nonblocking`` and ``nonallocating`` include the ``noexcept`` guarantee, but 
the presence of either
+attribute does not implicitly specify ``noexcept``. (It would be inappropriate 
for a Clang 
+attribute, ignored by non-Clang compilers, to imply a standard language 
feature.)
+
+``nonblocking(true)`` and ``nonallocating(true)`` apply to function *types*, 
and by extension, to
+function-like declarations. When applied to a declaration with a body, the 
compiler verifies the
+function, as described in the section "Analysis and warnings", below. 
Functions without an explicit
+performance constraint are not verified.
+
+``nonblocking(false)`` and ``nonallocating(false)`` are synonyms for the 
attributes ``blocking`` and
+``allocating``. They can be used on a function-like declaration to explicitly 
disable any potential
+inference of ``nonblocking`` or ``nonallocating`` during verification. 
(Inference is described later
+in this document). ``nonblocking(false)`` and ``nonallocating(false)`` are 
legal, but superfluous 
+when applied to a function *type*. ``float (int) [[nonblocking(false)]]`` and 
``float (int)`` are
+identical types.
+
+For all functions with no explicit performance constraint, the worst is 
assumed, that the function
+allocates memory an

[clang] Add clang/docs/FunctionEffectAnalysis.rst. (PR #109855)

2024-10-28 Thread Doug Wyatt via cfe-commits

dougsonos wrote:

@Sirraide, thanks for the excellent feedback. I've incorporated all of your 
suggestions.

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


[clang] Add clang/docs/FunctionEffectAnalysis.rst. (PR #109855)

2024-10-28 Thread Doug Wyatt via cfe-commits

https://github.com/dougsonos updated 
https://github.com/llvm/llvm-project/pull/109855

>From 085965b324efde41168c5d51db3a368578d3458f Mon Sep 17 00:00:00 2001
From: Doug Wyatt 
Date: Mon, 23 Sep 2024 14:44:32 -0700
Subject: [PATCH 1/6] Add clang/docs/FunctionEffectAnalysis.rst.

---
 clang/docs/FunctionEffectAnalysis.rst | 503 ++
 clang/docs/index.rst  |   1 +
 2 files changed, 504 insertions(+)
 create mode 100644 clang/docs/FunctionEffectAnalysis.rst

diff --git a/clang/docs/FunctionEffectAnalysis.rst 
b/clang/docs/FunctionEffectAnalysis.rst
new file mode 100644
index 00..8c1cf8a483c5f1
--- /dev/null
+++ b/clang/docs/FunctionEffectAnalysis.rst
@@ -0,0 +1,503 @@
+
+Function Effect Analysis
+
+
+Introduction
+
+
+Clang Function Effect Analysis is a C++ language extension which can warn 
about "unsafe"
+constructs. The feature is currently tailored for the Performance Constraint 
attributes,
+``nonblocking`` and ``nonallocating``; functions with these attributes are 
verified as not
+containing any language constructs or calls to other functions which violate 
the constraint.
+(See :doc:`AttributeReference`.)
+
+
+The ``nonblocking`` and ``nonallocating`` attributes
+
+
+Attribute syntax
+
+
+The ``nonblocking`` and ``nonallocating`` attributes apply to function types, 
allowing them to be
+attached to functions, blocks, function pointers, lambdas, and member 
functions.
+
+.. code-block:: c++
+
+  // Functions
+  void nonblockingFunction() [[clang::nonblocking]];
+  void nonallocatingFunction() [[clang::nonallocating]];
+
+  // Function pointers
+  void (*nonblockingFunctionPtr)() [[clang::nonblocking]];
+
+  // Typedefs, type aliases.
+  typedef void (*NBFunctionPtrTypedef)() [[clang::nonblocking]];
+  using NBFunctionPtrTypeAlias_gnu = __attribute__((nonblocking)) void (*)();
+  using NBFunctionPtrTypeAlias_std = void (*)() [[clang::nonblocking]];
+
+  // C++ methods
+  struct Struct {
+void NBMethod() [[clang::nonblocking]];
+  };
+
+  // C++ lambdas
+  auto nbLambda = []() [[clang::nonblocking]] {};
+
+  // Blocks
+  void (^nbBlock)() = ^() [[clang::nonblocking]] {};
+
+The attribute applies only to the function itself. In particular, it does not 
apply to any nested
+functions or declarations, such as blocks, lambdas, and local classes.
+
+This document uses the C++/C23 syntax ``[[clang::nonblocking]]``, since it 
parallels the placement 
+of the ``noexcept`` specifier, and the attributes have other similarities to 
``noexcept``. The GNU
+``__attribute__((nonblocking))`` syntax is also supported. Note that it 
requires a different 
+placement on a C++ type alias.
+
+Like ``noexcept``, ``nonblocking`` and ``nonallocating`` have an optional 
argument, a compile-time
+constant boolean expression. By default, the argument is true, so 
``[[clang::nonblocking(true)]]``
+is equivalent to ``[[clang::nonblocking]]``, and declares the function type as 
never locking.
+
+
+Attribute semantics
+---
+
+Together with ``noexcept``, the ``nonallocating`` and ``nonblocking`` 
attributes define an ordered
+series of performance constraints. From weakest to strongest:
+
+- ``noexcept`` (as per the C++ standard): The function type will never throw 
an exception.
+- ``nonallocating``: The function type will never allocate memory on the heap, 
and never throw an
+  exception.
+- ``nonblocking``: The function type will never block on a lock, never 
allocate memory on the heap,
+  and never throw an exception.
+
+``nonblocking`` includes the ``nonallocating`` guarantee. 
+
+``nonblocking`` and ``nonallocating`` include the ``noexcept`` guarantee, but 
the presence of either
+attribute does not implicitly specify ``noexcept``. (It would be inappropriate 
for a Clang 
+attribute, ignored by non-Clang compilers, to imply a standard language 
feature.)
+
+``nonblocking(true)`` and ``nonallocating(true)`` apply to function *types*, 
and by extension, to
+function-like declarations. When applied to a declaration with a body, the 
compiler verifies the
+function, as described in the section "Analysis and warnings", below. 
Functions without an explicit
+performance constraint are not verified.
+
+``nonblocking(false)`` and ``nonallocating(false)`` are synonyms for the 
attributes ``blocking`` and
+``allocating``. They can be used on a function-like declaration to explicitly 
disable any potential
+inference of ``nonblocking`` or ``nonallocating`` during verification. 
(Inference is described later
+in this document). ``nonblocking(false)`` and ``nonallocating(false)`` are 
legal, but superfluous 
+when applied to a function *type*. ``float (int) [[nonblocking(false)]]`` and 
``float (int)`` are
+identical types.
+
+For all functions with no explicit performance constraint, the worst is 
assumed, that the function
+allocates memory an

[clang] Add clang/docs/FunctionEffectAnalysis.rst. (PR #109855)

2024-10-26 Thread Doug Wyatt via cfe-commits

dougsonos wrote:

Ping

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


[clang] [Clang] Diagnose additional ObjC statements as function effect violations (PR #112148)

2024-10-14 Thread Doug Wyatt via cfe-commits

https://github.com/dougsonos updated 
https://github.com/llvm/llvm-project/pull/112148

>From f34e4ac55d04bcd8e34bef57afec5a39fbf2acb9 Mon Sep 17 00:00:00 2001
From: Doug Wyatt 
Date: Sun, 13 Oct 2024 11:35:10 -0700
Subject: [PATCH 1/3] [Clang] Diagnose ObjC @autoreleasepool statements as
 function effect violations.

---
 clang/lib/Sema/SemaFunctionEffects.cpp| 10 ++
 clang/test/SemaObjCXX/attr-nonblocking-constraints.mm |  5 +
 2 files changed, 15 insertions(+)

diff --git a/clang/lib/Sema/SemaFunctionEffects.cpp 
b/clang/lib/Sema/SemaFunctionEffects.cpp
index 0ac5de29f66aa7..5c4c71e58c4edd 100644
--- a/clang/lib/Sema/SemaFunctionEffects.cpp
+++ b/clang/lib/Sema/SemaFunctionEffects.cpp
@@ -1140,6 +1140,16 @@ class Analyzer {
   return true;
 }
 
+bool VisitObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *ARP) {
+  // Under the hood, @autorelease (potentially?) allocates memory and
+  // invokes ObjC methods. We don't currently have memory allocation as
+  // a "language construct" but we do have ObjC messaging, so diagnose 
that.
+  diagnoseLanguageConstruct(FunctionEffect::FE_ExcludeObjCMessageSend,
+ViolationID::AccessesObjCMethodOrProperty,
+ARP->getBeginLoc());
+  return true;
+}
+
 bool VisitSEHExceptStmt(SEHExceptStmt *Exc) {
   diagnoseLanguageConstruct(FunctionEffect::FE_ExcludeCatch,
 ViolationID::ThrowsOrCatchesExceptions,
diff --git a/clang/test/SemaObjCXX/attr-nonblocking-constraints.mm 
b/clang/test/SemaObjCXX/attr-nonblocking-constraints.mm
index abd0938ac321af..93555048562850 100644
--- a/clang/test/SemaObjCXX/attr-nonblocking-constraints.mm
+++ b/clang/test/SemaObjCXX/attr-nonblocking-constraints.mm
@@ -24,3 +24,8 @@ void nb4() [[clang::nonblocking]] {
@catch (...) { // expected-warning {{function with 'nonblocking' 
attribute must not throw or catch exceptions}}
}
 }
+
+void nb5() [[clang::nonblocking]] {
+   @autoreleasepool { // expected-warning {{function with 'nonblocking' 
attribute must not access ObjC methods or properties}}
+   }
+}
\ No newline at end of file

>From 930e1fa44fa1ae1c0fd030312748dd7222d587ca Mon Sep 17 00:00:00 2001
From: Doug Wyatt 
Date: Sun, 13 Oct 2024 11:48:03 -0700
Subject: [PATCH 2/3] Also diagnose @finally and @synchronized.

---
 clang/lib/Sema/SemaFunctionEffects.cpp  | 17 +
 .../SemaObjCXX/attr-nonblocking-constraints.mm  |  8 
 2 files changed, 25 insertions(+)

diff --git a/clang/lib/Sema/SemaFunctionEffects.cpp 
b/clang/lib/Sema/SemaFunctionEffects.cpp
index 5c4c71e58c4edd..190a0fd77a3699 100644
--- a/clang/lib/Sema/SemaFunctionEffects.cpp
+++ b/clang/lib/Sema/SemaFunctionEffects.cpp
@@ -1133,6 +1133,13 @@ class Analyzer {
   return true;
 }
 
+bool VisitObjCAtFinallyStmt(ObjCAtFinallyStmt *Finally) {
+  diagnoseLanguageConstruct(FunctionEffect::FE_ExcludeCatch,
+ViolationID::ThrowsOrCatchesExceptions,
+Finally->getAtFinallyLoc());
+  return true;
+}
+
 bool VisitObjCMessageExpr(ObjCMessageExpr *Msg) {
   diagnoseLanguageConstruct(FunctionEffect::FE_ExcludeObjCMessageSend,
 ViolationID::AccessesObjCMethodOrProperty,
@@ -1150,6 +1157,16 @@ class Analyzer {
   return true;
 }
 
+bool VisitObjCAtSynchronizedStmt(ObjCAtSynchronizedStmt *Sync) {
+  // Under the hood, this calls objc_sync_enter and objc_sync_exit, wrapped
+  // in a @try/@finally block. Diagnose this somewhat generically as "ObjC"
+  // messaging.
+  diagnoseLanguageConstruct(FunctionEffect::FE_ExcludeObjCMessageSend,
+ViolationID::AccessesObjCMethodOrProperty,
+Sync->getBeginLoc());
+  return true;
+}
+
 bool VisitSEHExceptStmt(SEHExceptStmt *Exc) {
   diagnoseLanguageConstruct(FunctionEffect::FE_ExcludeCatch,
 ViolationID::ThrowsOrCatchesExceptions,
diff --git a/clang/test/SemaObjCXX/attr-nonblocking-constraints.mm 
b/clang/test/SemaObjCXX/attr-nonblocking-constraints.mm
index 93555048562850..cb8c7c3f3d07c0 100644
--- a/clang/test/SemaObjCXX/attr-nonblocking-constraints.mm
+++ b/clang/test/SemaObjCXX/attr-nonblocking-constraints.mm
@@ -23,9 +23,17 @@ void nb4() [[clang::nonblocking]] {
}
@catch (...) { // expected-warning {{function with 'nonblocking' 
attribute must not throw or catch exceptions}}
}
+   @finally { // expected-warning {{function with 'nonblocking' attribute 
must not throw or catch exceptions}}
+   }
 }
 
+@class Lock;
+extern Lock *someLock;
+
 void nb5() [[clang::nonblocking]] {
@autoreleasepool { // expected-warning {{function with 'nonblocking' 
attribute must not access ObjC methods or properties}}
}
+
+   @synchronized(someLock) {

[clang] [Clang] Diagnose additional ObjC statements as function effect violations (PR #112148)

2024-10-13 Thread Doug Wyatt via cfe-commits

https://github.com/dougsonos created 
https://github.com/llvm/llvm-project/pull/112148

Bug fix: `@autoreleasepool`, `@synchronized`, and `@finally` were not being 
noticed and treated as function effect violations.

>From f34e4ac55d04bcd8e34bef57afec5a39fbf2acb9 Mon Sep 17 00:00:00 2001
From: Doug Wyatt 
Date: Sun, 13 Oct 2024 11:35:10 -0700
Subject: [PATCH 1/2] [Clang] Diagnose ObjC @autoreleasepool statements as
 function effect violations.

---
 clang/lib/Sema/SemaFunctionEffects.cpp| 10 ++
 clang/test/SemaObjCXX/attr-nonblocking-constraints.mm |  5 +
 2 files changed, 15 insertions(+)

diff --git a/clang/lib/Sema/SemaFunctionEffects.cpp 
b/clang/lib/Sema/SemaFunctionEffects.cpp
index 0ac5de29f66aa7..5c4c71e58c4edd 100644
--- a/clang/lib/Sema/SemaFunctionEffects.cpp
+++ b/clang/lib/Sema/SemaFunctionEffects.cpp
@@ -1140,6 +1140,16 @@ class Analyzer {
   return true;
 }
 
+bool VisitObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *ARP) {
+  // Under the hood, @autorelease (potentially?) allocates memory and
+  // invokes ObjC methods. We don't currently have memory allocation as
+  // a "language construct" but we do have ObjC messaging, so diagnose 
that.
+  diagnoseLanguageConstruct(FunctionEffect::FE_ExcludeObjCMessageSend,
+ViolationID::AccessesObjCMethodOrProperty,
+ARP->getBeginLoc());
+  return true;
+}
+
 bool VisitSEHExceptStmt(SEHExceptStmt *Exc) {
   diagnoseLanguageConstruct(FunctionEffect::FE_ExcludeCatch,
 ViolationID::ThrowsOrCatchesExceptions,
diff --git a/clang/test/SemaObjCXX/attr-nonblocking-constraints.mm 
b/clang/test/SemaObjCXX/attr-nonblocking-constraints.mm
index abd0938ac321af..93555048562850 100644
--- a/clang/test/SemaObjCXX/attr-nonblocking-constraints.mm
+++ b/clang/test/SemaObjCXX/attr-nonblocking-constraints.mm
@@ -24,3 +24,8 @@ void nb4() [[clang::nonblocking]] {
@catch (...) { // expected-warning {{function with 'nonblocking' 
attribute must not throw or catch exceptions}}
}
 }
+
+void nb5() [[clang::nonblocking]] {
+   @autoreleasepool { // expected-warning {{function with 'nonblocking' 
attribute must not access ObjC methods or properties}}
+   }
+}
\ No newline at end of file

>From 930e1fa44fa1ae1c0fd030312748dd7222d587ca Mon Sep 17 00:00:00 2001
From: Doug Wyatt 
Date: Sun, 13 Oct 2024 11:48:03 -0700
Subject: [PATCH 2/2] Also diagnose @finally and @synchronized.

---
 clang/lib/Sema/SemaFunctionEffects.cpp  | 17 +
 .../SemaObjCXX/attr-nonblocking-constraints.mm  |  8 
 2 files changed, 25 insertions(+)

diff --git a/clang/lib/Sema/SemaFunctionEffects.cpp 
b/clang/lib/Sema/SemaFunctionEffects.cpp
index 5c4c71e58c4edd..190a0fd77a3699 100644
--- a/clang/lib/Sema/SemaFunctionEffects.cpp
+++ b/clang/lib/Sema/SemaFunctionEffects.cpp
@@ -1133,6 +1133,13 @@ class Analyzer {
   return true;
 }
 
+bool VisitObjCAtFinallyStmt(ObjCAtFinallyStmt *Finally) {
+  diagnoseLanguageConstruct(FunctionEffect::FE_ExcludeCatch,
+ViolationID::ThrowsOrCatchesExceptions,
+Finally->getAtFinallyLoc());
+  return true;
+}
+
 bool VisitObjCMessageExpr(ObjCMessageExpr *Msg) {
   diagnoseLanguageConstruct(FunctionEffect::FE_ExcludeObjCMessageSend,
 ViolationID::AccessesObjCMethodOrProperty,
@@ -1150,6 +1157,16 @@ class Analyzer {
   return true;
 }
 
+bool VisitObjCAtSynchronizedStmt(ObjCAtSynchronizedStmt *Sync) {
+  // Under the hood, this calls objc_sync_enter and objc_sync_exit, wrapped
+  // in a @try/@finally block. Diagnose this somewhat generically as "ObjC"
+  // messaging.
+  diagnoseLanguageConstruct(FunctionEffect::FE_ExcludeObjCMessageSend,
+ViolationID::AccessesObjCMethodOrProperty,
+Sync->getBeginLoc());
+  return true;
+}
+
 bool VisitSEHExceptStmt(SEHExceptStmt *Exc) {
   diagnoseLanguageConstruct(FunctionEffect::FE_ExcludeCatch,
 ViolationID::ThrowsOrCatchesExceptions,
diff --git a/clang/test/SemaObjCXX/attr-nonblocking-constraints.mm 
b/clang/test/SemaObjCXX/attr-nonblocking-constraints.mm
index 93555048562850..cb8c7c3f3d07c0 100644
--- a/clang/test/SemaObjCXX/attr-nonblocking-constraints.mm
+++ b/clang/test/SemaObjCXX/attr-nonblocking-constraints.mm
@@ -23,9 +23,17 @@ void nb4() [[clang::nonblocking]] {
}
@catch (...) { // expected-warning {{function with 'nonblocking' 
attribute must not throw or catch exceptions}}
}
+   @finally { // expected-warning {{function with 'nonblocking' attribute 
must not throw or catch exceptions}}
+   }
 }
 
+@class Lock;
+extern Lock *someLock;
+
 void nb5() [[clang::nonblocking]] {
@autoreleasepool { // expected-warning {{

[clang] Rough first stab at addressing #85120 (PR #85147)

2024-10-13 Thread Doug Wyatt via cfe-commits

https://github.com/dougsonos closed 
https://github.com/llvm/llvm-project/pull/85147
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] Add clang/docs/FunctionEffectAnalysis.rst. (PR #109855)

2024-10-11 Thread Doug Wyatt via cfe-commits

https://github.com/dougsonos updated 
https://github.com/llvm/llvm-project/pull/109855

>From 085965b324efde41168c5d51db3a368578d3458f Mon Sep 17 00:00:00 2001
From: Doug Wyatt 
Date: Mon, 23 Sep 2024 14:44:32 -0700
Subject: [PATCH 1/5] Add clang/docs/FunctionEffectAnalysis.rst.

---
 clang/docs/FunctionEffectAnalysis.rst | 503 ++
 clang/docs/index.rst  |   1 +
 2 files changed, 504 insertions(+)
 create mode 100644 clang/docs/FunctionEffectAnalysis.rst

diff --git a/clang/docs/FunctionEffectAnalysis.rst 
b/clang/docs/FunctionEffectAnalysis.rst
new file mode 100644
index 00..8c1cf8a483c5f1
--- /dev/null
+++ b/clang/docs/FunctionEffectAnalysis.rst
@@ -0,0 +1,503 @@
+
+Function Effect Analysis
+
+
+Introduction
+
+
+Clang Function Effect Analysis is a C++ language extension which can warn 
about "unsafe"
+constructs. The feature is currently tailored for the Performance Constraint 
attributes,
+``nonblocking`` and ``nonallocating``; functions with these attributes are 
verified as not
+containing any language constructs or calls to other functions which violate 
the constraint.
+(See :doc:`AttributeReference`.)
+
+
+The ``nonblocking`` and ``nonallocating`` attributes
+
+
+Attribute syntax
+
+
+The ``nonblocking`` and ``nonallocating`` attributes apply to function types, 
allowing them to be
+attached to functions, blocks, function pointers, lambdas, and member 
functions.
+
+.. code-block:: c++
+
+  // Functions
+  void nonblockingFunction() [[clang::nonblocking]];
+  void nonallocatingFunction() [[clang::nonallocating]];
+
+  // Function pointers
+  void (*nonblockingFunctionPtr)() [[clang::nonblocking]];
+
+  // Typedefs, type aliases.
+  typedef void (*NBFunctionPtrTypedef)() [[clang::nonblocking]];
+  using NBFunctionPtrTypeAlias_gnu = __attribute__((nonblocking)) void (*)();
+  using NBFunctionPtrTypeAlias_std = void (*)() [[clang::nonblocking]];
+
+  // C++ methods
+  struct Struct {
+void NBMethod() [[clang::nonblocking]];
+  };
+
+  // C++ lambdas
+  auto nbLambda = []() [[clang::nonblocking]] {};
+
+  // Blocks
+  void (^nbBlock)() = ^() [[clang::nonblocking]] {};
+
+The attribute applies only to the function itself. In particular, it does not 
apply to any nested
+functions or declarations, such as blocks, lambdas, and local classes.
+
+This document uses the C++/C23 syntax ``[[clang::nonblocking]]``, since it 
parallels the placement 
+of the ``noexcept`` specifier, and the attributes have other similarities to 
``noexcept``. The GNU
+``__attribute__((nonblocking))`` syntax is also supported. Note that it 
requires a different 
+placement on a C++ type alias.
+
+Like ``noexcept``, ``nonblocking`` and ``nonallocating`` have an optional 
argument, a compile-time
+constant boolean expression. By default, the argument is true, so 
``[[clang::nonblocking(true)]]``
+is equivalent to ``[[clang::nonblocking]]``, and declares the function type as 
never locking.
+
+
+Attribute semantics
+---
+
+Together with ``noexcept``, the ``nonallocating`` and ``nonblocking`` 
attributes define an ordered
+series of performance constraints. From weakest to strongest:
+
+- ``noexcept`` (as per the C++ standard): The function type will never throw 
an exception.
+- ``nonallocating``: The function type will never allocate memory on the heap, 
and never throw an
+  exception.
+- ``nonblocking``: The function type will never block on a lock, never 
allocate memory on the heap,
+  and never throw an exception.
+
+``nonblocking`` includes the ``nonallocating`` guarantee. 
+
+``nonblocking`` and ``nonallocating`` include the ``noexcept`` guarantee, but 
the presence of either
+attribute does not implicitly specify ``noexcept``. (It would be inappropriate 
for a Clang 
+attribute, ignored by non-Clang compilers, to imply a standard language 
feature.)
+
+``nonblocking(true)`` and ``nonallocating(true)`` apply to function *types*, 
and by extension, to
+function-like declarations. When applied to a declaration with a body, the 
compiler verifies the
+function, as described in the section "Analysis and warnings", below. 
Functions without an explicit
+performance constraint are not verified.
+
+``nonblocking(false)`` and ``nonallocating(false)`` are synonyms for the 
attributes ``blocking`` and
+``allocating``. They can be used on a function-like declaration to explicitly 
disable any potential
+inference of ``nonblocking`` or ``nonallocating`` during verification. 
(Inference is described later
+in this document). ``nonblocking(false)`` and ``nonallocating(false)`` are 
legal, but superfluous 
+when applied to a function *type*. ``float (int) [[nonblocking(false)]]`` and 
``float (int)`` are
+identical types.
+
+For all functions with no explicit performance constraint, the worst is 
assumed, that the function
+allocates memory an

[clang] Effect analysis: correctly detect `(x ? a : b)` as nonblocking when a and b are (PR #111224)

2024-10-08 Thread Doug Wyatt via cfe-commits

dougsonos wrote:

> Oh, also, are there any other places where we should maybe be using 
> `FunctionEffectsRef::get()`?

Good question. I looked for other uses of `QualType` and `getType()` in 
SemaFunctionEffects, but this is the only spot (`checkIndirectCall`) where we 
are interested in obtaining effects from an expression which didn't come from a 
`Decl`. If there were a `Decl` it would have taken an earlier branch in 
`VisitCallExpr`, where we build a `CallableInfo`, whose constructor calls 
`FunctionEffectsRef::get()` in the case of an indirect call through a variable 
which has a `Decl`. So I think we're good.

Thanks for the quick review.

More patches? My favorite of Murphy's Laws of Programming is "if it is useful, 
it will have to be changed."

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


[clang] Effect analysis: correctly detect `(x ? a : b)` as nonblocking when a and b are (PR #111224)

2024-10-07 Thread Doug Wyatt via cfe-commits

dougsonos wrote:

@cjappl @Sirraide would appreciate what I hope will be a quick review on a 
small fix.

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


[clang] Effect analysis: correctly detect `(x ? a : b)` as nonblocking when a and b are (PR #111224)

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

https://github.com/dougsonos updated 
https://github.com/llvm/llvm-project/pull/111224

>From 02398e6398892dac5d151a6e425bf107213e12a8 Mon Sep 17 00:00:00 2001
From: Doug Wyatt 
Date: Fri, 4 Oct 2024 18:28:37 -0700
Subject: [PATCH 1/3] Effect analysis: correctly detect `(f ? a : b)` as
 nonblocking when a and b are.

---
 clang/lib/Sema/SemaFunctionEffects.cpp   | 11 +--
 clang/test/Sema/attr-nonblocking-constraints.cpp | 10 ++
 2 files changed, 15 insertions(+), 6 deletions(-)

diff --git a/clang/lib/Sema/SemaFunctionEffects.cpp 
b/clang/lib/Sema/SemaFunctionEffects.cpp
index 0fb18d207a50ba..0ac5de29f66aa7 100644
--- a/clang/lib/Sema/SemaFunctionEffects.cpp
+++ b/clang/lib/Sema/SemaFunctionEffects.cpp
@@ -1048,15 +1048,14 @@ class Analyzer {
 }
 
 void checkIndirectCall(CallExpr *Call, QualType CalleeType) {
-  auto *FPT =
-  CalleeType->getAs(); // Null if FunctionType.
   FunctionEffectKindSet CalleeEffects;
-  if (FPT)
-CalleeEffects.insert(FPT->getFunctionEffects());
+  if (FunctionEffectsRef Effects = FunctionEffectsRef::get(CalleeType);
+  !Effects.empty())
+CalleeEffects.insert(Effects);
 
   auto Check1Effect = [&](FunctionEffect Effect, bool Inferring) {
-if (FPT == nullptr || Effect.shouldDiagnoseFunctionCall(
-  /*direct=*/false, CalleeEffects))
+if (Effect.shouldDiagnoseFunctionCall(
+/*direct=*/false, CalleeEffects))
   addViolation(Inferring, Effect, ViolationID::CallsExprWithoutEffect,
Call->getBeginLoc());
   };
diff --git a/clang/test/Sema/attr-nonblocking-constraints.cpp 
b/clang/test/Sema/attr-nonblocking-constraints.cpp
index c694860069c960..ff8caf0e573403 100644
--- a/clang/test/Sema/attr-nonblocking-constraints.cpp
+++ b/clang/test/Sema/attr-nonblocking-constraints.cpp
@@ -156,6 +156,16 @@ void nb10(
static_cast(fp1)(); // expected-warning {{function with 
'nonblocking' attribute must not call non-'nonblocking' expression}}
 }
 
+// Expression involving indirection
+int nb10a() [[clang::nonblocking]];
+int nb10b() [[clang::nonblocking]];
+
+int nb10c(bool x) [[clang::nonblocking]]
+{
+   // Warns that the expression is not nonblocking.
+   return (x ? nb10a : nb10b)();
+}
+
 // Interactions with nonblocking(false)
 void nb11_no_inference_1() [[clang::nonblocking(false)]] // expected-note 
{{function does not permit inference of 'nonblocking'}}
 {

>From 1dece8572d01d86a8d42c0985ca4058ec4838064 Mon Sep 17 00:00:00 2001
From: Doug Wyatt 
Date: Fri, 4 Oct 2024 18:40:01 -0700
Subject: [PATCH 2/3] Fix comment in test.

---
 clang/test/Sema/attr-nonblocking-constraints.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/clang/test/Sema/attr-nonblocking-constraints.cpp 
b/clang/test/Sema/attr-nonblocking-constraints.cpp
index ff8caf0e573403..59061dffa372b4 100644
--- a/clang/test/Sema/attr-nonblocking-constraints.cpp
+++ b/clang/test/Sema/attr-nonblocking-constraints.cpp
@@ -162,8 +162,7 @@ int nb10b() [[clang::nonblocking]];
 
 int nb10c(bool x) [[clang::nonblocking]]
 {
-   // Warns that the expression is not nonblocking.
-   return (x ? nb10a : nb10b)();
+   return (x ? nb10a : nb10b)(); // No diagnostic.
 }
 
 // Interactions with nonblocking(false)

>From 30c453ec77dbd3a2b01dae032506e808cf26470c Mon Sep 17 00:00:00 2001
From: Doug Wyatt 
Date: Sat, 5 Oct 2024 09:34:27 -0700
Subject: [PATCH 3/3] add another test

---
 clang/test/Sema/attr-nonblocking-constraints.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/clang/test/Sema/attr-nonblocking-constraints.cpp 
b/clang/test/Sema/attr-nonblocking-constraints.cpp
index 59061dffa372b4..f23093d4dc8a96 100644
--- a/clang/test/Sema/attr-nonblocking-constraints.cpp
+++ b/clang/test/Sema/attr-nonblocking-constraints.cpp
@@ -159,9 +159,11 @@ void nb10(
 // Expression involving indirection
 int nb10a() [[clang::nonblocking]];
 int nb10b() [[clang::nonblocking]];
+int blocking();
 
 int nb10c(bool x) [[clang::nonblocking]]
 {
+   int y = (x ? nb10a : blocking)(); // expected-warning {{attribute 
'nonblocking' should not be added via type conversion}}
return (x ? nb10a : nb10b)(); // No diagnostic.
 }
 

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] Effect analysis: correctly detect `(x ? a : b)` as nonblocking when a and b are (PR #111224)

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

https://github.com/dougsonos deleted 
https://github.com/llvm/llvm-project/pull/111224
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] Effect analysis: correctly detect `(x ? a : b)` as nonblocking when a and b are (PR #111224)

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

https://github.com/dougsonos updated 
https://github.com/llvm/llvm-project/pull/111224

>From 02398e6398892dac5d151a6e425bf107213e12a8 Mon Sep 17 00:00:00 2001
From: Doug Wyatt 
Date: Fri, 4 Oct 2024 18:28:37 -0700
Subject: [PATCH 1/2] Effect analysis: correctly detect `(f ? a : b)` as
 nonblocking when a and b are.

---
 clang/lib/Sema/SemaFunctionEffects.cpp   | 11 +--
 clang/test/Sema/attr-nonblocking-constraints.cpp | 10 ++
 2 files changed, 15 insertions(+), 6 deletions(-)

diff --git a/clang/lib/Sema/SemaFunctionEffects.cpp 
b/clang/lib/Sema/SemaFunctionEffects.cpp
index 0fb18d207a50ba..0ac5de29f66aa7 100644
--- a/clang/lib/Sema/SemaFunctionEffects.cpp
+++ b/clang/lib/Sema/SemaFunctionEffects.cpp
@@ -1048,15 +1048,14 @@ class Analyzer {
 }
 
 void checkIndirectCall(CallExpr *Call, QualType CalleeType) {
-  auto *FPT =
-  CalleeType->getAs(); // Null if FunctionType.
   FunctionEffectKindSet CalleeEffects;
-  if (FPT)
-CalleeEffects.insert(FPT->getFunctionEffects());
+  if (FunctionEffectsRef Effects = FunctionEffectsRef::get(CalleeType);
+  !Effects.empty())
+CalleeEffects.insert(Effects);
 
   auto Check1Effect = [&](FunctionEffect Effect, bool Inferring) {
-if (FPT == nullptr || Effect.shouldDiagnoseFunctionCall(
-  /*direct=*/false, CalleeEffects))
+if (Effect.shouldDiagnoseFunctionCall(
+/*direct=*/false, CalleeEffects))
   addViolation(Inferring, Effect, ViolationID::CallsExprWithoutEffect,
Call->getBeginLoc());
   };
diff --git a/clang/test/Sema/attr-nonblocking-constraints.cpp 
b/clang/test/Sema/attr-nonblocking-constraints.cpp
index c694860069c960..ff8caf0e573403 100644
--- a/clang/test/Sema/attr-nonblocking-constraints.cpp
+++ b/clang/test/Sema/attr-nonblocking-constraints.cpp
@@ -156,6 +156,16 @@ void nb10(
static_cast(fp1)(); // expected-warning {{function with 
'nonblocking' attribute must not call non-'nonblocking' expression}}
 }
 
+// Expression involving indirection
+int nb10a() [[clang::nonblocking]];
+int nb10b() [[clang::nonblocking]];
+
+int nb10c(bool x) [[clang::nonblocking]]
+{
+   // Warns that the expression is not nonblocking.
+   return (x ? nb10a : nb10b)();
+}
+
 // Interactions with nonblocking(false)
 void nb11_no_inference_1() [[clang::nonblocking(false)]] // expected-note 
{{function does not permit inference of 'nonblocking'}}
 {

>From 1dece8572d01d86a8d42c0985ca4058ec4838064 Mon Sep 17 00:00:00 2001
From: Doug Wyatt 
Date: Fri, 4 Oct 2024 18:40:01 -0700
Subject: [PATCH 2/2] Fix comment in test.

---
 clang/test/Sema/attr-nonblocking-constraints.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/clang/test/Sema/attr-nonblocking-constraints.cpp 
b/clang/test/Sema/attr-nonblocking-constraints.cpp
index ff8caf0e573403..59061dffa372b4 100644
--- a/clang/test/Sema/attr-nonblocking-constraints.cpp
+++ b/clang/test/Sema/attr-nonblocking-constraints.cpp
@@ -162,8 +162,7 @@ int nb10b() [[clang::nonblocking]];
 
 int nb10c(bool x) [[clang::nonblocking]]
 {
-   // Warns that the expression is not nonblocking.
-   return (x ? nb10a : nb10b)();
+   return (x ? nb10a : nb10b)(); // No diagnostic.
 }
 
 // Interactions with nonblocking(false)

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] Effect analysis: correctly detect `(x ? a : b)` as nonblocking when a and b are (PR #111224)

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

https://github.com/dougsonos edited 
https://github.com/llvm/llvm-project/pull/111224
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] Effect analysis: correctly detect `(x ? a : b)` as nonblocking when a and b are (PR #111224)

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

https://github.com/dougsonos edited 
https://github.com/llvm/llvm-project/pull/111224
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] Effect analysis: correctly detect `(f ? a : b)` as nonblocking when a and b are (PR #111224)

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


@@ -1048,15 +1048,14 @@ class Analyzer {
 }
 
 void checkIndirectCall(CallExpr *Call, QualType CalleeType) {
-  auto *FPT =
-  CalleeType->getAs(); // Null if FunctionType.
   FunctionEffectKindSet CalleeEffects;
-  if (FPT)
-CalleeEffects.insert(FPT->getFunctionEffects());
+  if (FunctionEffectsRef Effects = FunctionEffectsRef::get(CalleeType);
+  !Effects.empty())
+CalleeEffects.insert(Effects);
 
   auto Check1Effect = [&](FunctionEffect Effect, bool Inferring) {
-if (FPT == nullptr || Effect.shouldDiagnoseFunctionCall(
-  /*direct=*/false, CalleeEffects))
+if (Effect.shouldDiagnoseFunctionCall(

dougsonos wrote:

The check for a null FPT is/was superfluous; in that case the callee never has 
any declared effects and `shouldDiagnoseFunctionCall` will complain.

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


[clang] Effect analysis: correctly detect `(f ? a : b)` as nonblocking when a and b are (PR #111224)

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


@@ -1048,15 +1048,14 @@ class Analyzer {
 }
 
 void checkIndirectCall(CallExpr *Call, QualType CalleeType) {
-  auto *FPT =
-  CalleeType->getAs(); // Null if FunctionType.
   FunctionEffectKindSet CalleeEffects;
-  if (FPT)
-CalleeEffects.insert(FPT->getFunctionEffects());
+  if (FunctionEffectsRef Effects = FunctionEffectsRef::get(CalleeType);

dougsonos wrote:

`FunctionEffectsRef::get()` knows how to pick through indirections 
(references/pointers) to get to a `FunctionProtoType` so just reuse that.

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


[clang] Effect analysis: correctly detect `(f ? a : b)` as nonblocking when a and b are (PR #111224)

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


@@ -156,6 +156,16 @@ void nb10(
static_cast(fp1)(); // expected-warning {{function with 
'nonblocking' attribute must not call non-'nonblocking' expression}}
 }
 
+// Expression involving indirection
+int nb10a() [[clang::nonblocking]];
+int nb10b() [[clang::nonblocking]];
+
+int nb10c(bool x) [[clang::nonblocking]]
+{
+   // Warns that the expression is not nonblocking.

dougsonos wrote:

Oops that comment is left over from when the test was failing on main :)

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


[clang] Effect analysis: correctly detect `(f ? a : b)` as nonblocking when a and b are (PR #111224)

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

https://github.com/dougsonos created 
https://github.com/llvm/llvm-project/pull/111224

Fix: Effect analysis: correctly detect `(f ? a : b)` as nonblocking when a and 
b are

>From 02398e6398892dac5d151a6e425bf107213e12a8 Mon Sep 17 00:00:00 2001
From: Doug Wyatt 
Date: Fri, 4 Oct 2024 18:28:37 -0700
Subject: [PATCH] Effect analysis: correctly detect `(f ? a : b)` as
 nonblocking when a and b are.

---
 clang/lib/Sema/SemaFunctionEffects.cpp   | 11 +--
 clang/test/Sema/attr-nonblocking-constraints.cpp | 10 ++
 2 files changed, 15 insertions(+), 6 deletions(-)

diff --git a/clang/lib/Sema/SemaFunctionEffects.cpp 
b/clang/lib/Sema/SemaFunctionEffects.cpp
index 0fb18d207a50ba..0ac5de29f66aa7 100644
--- a/clang/lib/Sema/SemaFunctionEffects.cpp
+++ b/clang/lib/Sema/SemaFunctionEffects.cpp
@@ -1048,15 +1048,14 @@ class Analyzer {
 }
 
 void checkIndirectCall(CallExpr *Call, QualType CalleeType) {
-  auto *FPT =
-  CalleeType->getAs(); // Null if FunctionType.
   FunctionEffectKindSet CalleeEffects;
-  if (FPT)
-CalleeEffects.insert(FPT->getFunctionEffects());
+  if (FunctionEffectsRef Effects = FunctionEffectsRef::get(CalleeType);
+  !Effects.empty())
+CalleeEffects.insert(Effects);
 
   auto Check1Effect = [&](FunctionEffect Effect, bool Inferring) {
-if (FPT == nullptr || Effect.shouldDiagnoseFunctionCall(
-  /*direct=*/false, CalleeEffects))
+if (Effect.shouldDiagnoseFunctionCall(
+/*direct=*/false, CalleeEffects))
   addViolation(Inferring, Effect, ViolationID::CallsExprWithoutEffect,
Call->getBeginLoc());
   };
diff --git a/clang/test/Sema/attr-nonblocking-constraints.cpp 
b/clang/test/Sema/attr-nonblocking-constraints.cpp
index c694860069c960..ff8caf0e573403 100644
--- a/clang/test/Sema/attr-nonblocking-constraints.cpp
+++ b/clang/test/Sema/attr-nonblocking-constraints.cpp
@@ -156,6 +156,16 @@ void nb10(
static_cast(fp1)(); // expected-warning {{function with 
'nonblocking' attribute must not call non-'nonblocking' expression}}
 }
 
+// Expression involving indirection
+int nb10a() [[clang::nonblocking]];
+int nb10b() [[clang::nonblocking]];
+
+int nb10c(bool x) [[clang::nonblocking]]
+{
+   // Warns that the expression is not nonblocking.
+   return (x ? nb10a : nb10b)();
+}
+
 // Interactions with nonblocking(false)
 void nb11_no_inference_1() [[clang::nonblocking(false)]] // expected-note 
{{function does not permit inference of 'nonblocking'}}
 {

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] Add clang/docs/FunctionEffectAnalysis.rst. (PR #109855)

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

dougsonos wrote:

Ping

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


[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

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

dougsonos wrote:

Thank you @Sirraide @erichkeane and all, for your patient, careful and thorough 
reviews!

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


[clang] Add clang/docs/FunctionEffectAnalysis.rst. (PR #109855)

2024-09-26 Thread Doug Wyatt via cfe-commits


@@ -0,0 +1,503 @@
+
+Function Effect Analysis
+
+
+Introduction
+
+
+Clang Function Effect Analysis is a C++ language extension which can warn 
about "unsafe"
+constructs. The feature is currently tailored for the Performance Constraint 
attributes,
+``nonblocking`` and ``nonallocating``; functions with these attributes are 
verified as not
+containing any language constructs or calls to other functions which violate 
the constraint.
+(See :doc:`AttributeReference`.)
+
+
+The ``nonblocking`` and ``nonallocating`` attributes
+
+
+Attribute syntax
+
+
+The ``nonblocking`` and ``nonallocating`` attributes apply to function types, 
allowing them to be
+attached to functions, blocks, function pointers, lambdas, and member 
functions.
+
+.. code-block:: c++
+
+  // Functions
+  void nonblockingFunction() [[clang::nonblocking]];
+  void nonallocatingFunction() [[clang::nonallocating]];
+
+  // Function pointers
+  void (*nonblockingFunctionPtr)() [[clang::nonblocking]];
+
+  // Typedefs, type aliases.
+  typedef void (*NBFunctionPtrTypedef)() [[clang::nonblocking]];
+  using NBFunctionPtrTypeAlias_gnu = __attribute__((nonblocking)) void (*)();
+  using NBFunctionPtrTypeAlias_std = void (*)() [[clang::nonblocking]];
+
+  // C++ methods
+  struct Struct {
+void NBMethod() [[clang::nonblocking]];
+  };
+
+  // C++ lambdas
+  auto nbLambda = []() [[clang::nonblocking]] {};
+
+  // Blocks
+  void (^nbBlock)() = ^() [[clang::nonblocking]] {};
+
+The attribute applies only to the function itself. In particular, it does not 
apply to any nested
+functions or declarations, such as blocks, lambdas, and local classes.
+
+This document uses the C++/C23 syntax ``[[clang::nonblocking]]``, since it 
parallels the placement 
+of the ``noexcept`` specifier, and the attributes have other similarities to 
``noexcept``. The GNU
+``__attribute__((nonblocking))`` syntax is also supported. Note that it 
requires a different 
+placement on a C++ type alias.
+
+Like ``noexcept``, ``nonblocking`` and ``nonallocating`` have an optional 
argument, a compile-time
+constant boolean expression. By default, the argument is true, so 
``[[clang::nonblocking(true)]]``
+is equivalent to ``[[clang::nonblocking]]``, and declares the function type as 
never locking.
+
+
+Attribute semantics
+---
+
+Together with ``noexcept``, the ``nonallocating`` and ``nonblocking`` 
attributes define an ordered
+series of performance constraints. From weakest to strongest:
+
+- ``noexcept`` (as per the C++ standard): The function type will never throw 
an exception.
+- ``nonallocating``: The function type will never allocate memory on the heap, 
and never throw an
+  exception.
+- ``nonblocking``: The function type will never block on a lock, never 
allocate memory on the heap,
+  and never throw an exception.
+
+``nonblocking`` includes the ``nonallocating`` guarantee. 
+
+``nonblocking`` and ``nonallocating`` include the ``noexcept`` guarantee, but 
the presence of either
+attribute does not implicitly specify ``noexcept``. (It would be inappropriate 
for a Clang 
+attribute, ignored by non-Clang compilers, to imply a standard language 
feature.)
+
+``nonblocking(true)`` and ``nonallocating(true)`` apply to function *types*, 
and by extension, to
+function-like declarations. When applied to a declaration with a body, the 
compiler verifies the
+function, as described in the section "Analysis and warnings", below. 
Functions without an explicit
+performance constraint are not verified.
+
+``nonblocking(false)`` and ``nonallocating(false)`` are synonyms for the 
attributes ``blocking`` and
+``allocating``. They can be used on a function-like declaration to explicitly 
disable any potential
+inference of ``nonblocking`` or ``nonallocating`` during verification. 
(Inference is described later
+in this document). ``nonblocking(false)`` and ``nonallocating(false)`` are 
legal, but superfluous 
+when applied to a function *type*. ``float (int) [[nonblocking(false)]]`` and 
``float (int)`` are
+identical types.
+
+For all functions with no explicit performance constraint, the worst is 
assumed, that the function
+allocates memory and potentially blocks, unless it can be inferred otherwise, 
as described in the
+discussion of verification.
+
+The following list describes the meanings of all permutations of the two 
attributes and arguments:
+
+- ``nonblocking(true)`` + ``nonallocating(true)``: valid; 
``nonallocating(true)`` is superfluous but
+  does not contradict the guarantee.
+- ``nonblocking(true)`` + ``nonallocating(false)``: error, contradictory.
+- ``nonblocking(false)`` + ``nonallocating(true)``: valid; the function does 
not allocate memory,
+  but may lock for other reasons.
+- ``nonblocking(false)`` + ``nonallocating(false)``: valid.
+
+Type conversions
+
+
+A performance constrain

[clang] Add clang/docs/FunctionEffectAnalysis.rst. (PR #109855)

2024-09-26 Thread Doug Wyatt via cfe-commits


@@ -0,0 +1,503 @@
+
+Function Effect Analysis
+
+
+Introduction
+
+
+Clang Function Effect Analysis is a C++ language extension which can warn 
about "unsafe"
+constructs. The feature is currently tailored for the Performance Constraint 
attributes,
+``nonblocking`` and ``nonallocating``; functions with these attributes are 
verified as not
+containing any language constructs or calls to other functions which violate 
the constraint.
+(See :doc:`AttributeReference`.)
+
+
+The ``nonblocking`` and ``nonallocating`` attributes
+
+
+Attribute syntax
+
+
+The ``nonblocking`` and ``nonallocating`` attributes apply to function types, 
allowing them to be
+attached to functions, blocks, function pointers, lambdas, and member 
functions.
+
+.. code-block:: c++
+
+  // Functions
+  void nonblockingFunction() [[clang::nonblocking]];
+  void nonallocatingFunction() [[clang::nonallocating]];
+
+  // Function pointers
+  void (*nonblockingFunctionPtr)() [[clang::nonblocking]];
+
+  // Typedefs, type aliases.
+  typedef void (*NBFunctionPtrTypedef)() [[clang::nonblocking]];
+  using NBFunctionPtrTypeAlias_gnu = __attribute__((nonblocking)) void (*)();
+  using NBFunctionPtrTypeAlias_std = void (*)() [[clang::nonblocking]];
+
+  // C++ methods
+  struct Struct {
+void NBMethod() [[clang::nonblocking]];
+  };
+
+  // C++ lambdas
+  auto nbLambda = []() [[clang::nonblocking]] {};
+
+  // Blocks
+  void (^nbBlock)() = ^() [[clang::nonblocking]] {};
+
+The attribute applies only to the function itself. In particular, it does not 
apply to any nested
+functions or declarations, such as blocks, lambdas, and local classes.
+
+This document uses the C++/C23 syntax ``[[clang::nonblocking]]``, since it 
parallels the placement 
+of the ``noexcept`` specifier, and the attributes have other similarities to 
``noexcept``. The GNU
+``__attribute__((nonblocking))`` syntax is also supported. Note that it 
requires a different 
+placement on a C++ type alias.
+
+Like ``noexcept``, ``nonblocking`` and ``nonallocating`` have an optional 
argument, a compile-time
+constant boolean expression. By default, the argument is true, so 
``[[clang::nonblocking(true)]]``
+is equivalent to ``[[clang::nonblocking]]``, and declares the function type as 
never locking.
+
+
+Attribute semantics
+---
+
+Together with ``noexcept``, the ``nonallocating`` and ``nonblocking`` 
attributes define an ordered
+series of performance constraints. From weakest to strongest:
+
+- ``noexcept`` (as per the C++ standard): The function type will never throw 
an exception.
+- ``nonallocating``: The function type will never allocate memory on the heap, 
and never throw an
+  exception.
+- ``nonblocking``: The function type will never block on a lock, never 
allocate memory on the heap,
+  and never throw an exception.
+
+``nonblocking`` includes the ``nonallocating`` guarantee. 
+
+``nonblocking`` and ``nonallocating`` include the ``noexcept`` guarantee, but 
the presence of either
+attribute does not implicitly specify ``noexcept``. (It would be inappropriate 
for a Clang 
+attribute, ignored by non-Clang compilers, to imply a standard language 
feature.)
+
+``nonblocking(true)`` and ``nonallocating(true)`` apply to function *types*, 
and by extension, to
+function-like declarations. When applied to a declaration with a body, the 
compiler verifies the
+function, as described in the section "Analysis and warnings", below. 
Functions without an explicit
+performance constraint are not verified.
+
+``nonblocking(false)`` and ``nonallocating(false)`` are synonyms for the 
attributes ``blocking`` and
+``allocating``. They can be used on a function-like declaration to explicitly 
disable any potential
+inference of ``nonblocking`` or ``nonallocating`` during verification. 
(Inference is described later
+in this document). ``nonblocking(false)`` and ``nonallocating(false)`` are 
legal, but superfluous 
+when applied to a function *type*. ``float (int) [[nonblocking(false)]]`` and 
``float (int)`` are
+identical types.
+
+For all functions with no explicit performance constraint, the worst is 
assumed, that the function
+allocates memory and potentially blocks, unless it can be inferred otherwise, 
as described in the

dougsonos wrote:

(Though, I am in the middle of incorporating this round of feedback + fixing 
whatever else looks wrong...)

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


[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

2024-09-26 Thread Doug Wyatt via cfe-commits


@@ -571,6 +571,9 @@ New features
   if class of allocation and deallocation function mismatches.
   `Documentation 
`__.
 
+- Function effects (the ``nonblocking`` and ``nonallocating`` "performance 
constraint" attributes)

dougsonos wrote:

I took a shot at something more "hook-y".

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


[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

2024-09-26 Thread Doug Wyatt via cfe-commits


@@ -4712,12 +4715,13 @@ class FunctionEffect {
 public:
   /// Identifies the particular effect.
   enum class Kind : uint8_t {
-None = 0,
-NonBlocking = 1,
-NonAllocating = 2,
-Blocking = 3,
-Allocating = 4
+NonBlocking = 0,
+NonAllocating = 1,
+Blocking = 2,
+Allocating = 3,
+

dougsonos wrote:

`Last` seems to be the most common name for that last value...

```
enum class ComparisonCategoryType : unsigned char {
  PartialOrdering,
  WeakOrdering,
  StrongOrdering,
  First = PartialOrdering,
  Last = StrongOrdering
};

enum class Lang { C = 0, CXX, LastValue = CXX };

enum class SyncScope {
  SystemScope,
  DeviceScope,
  WorkgroupScope,
  WavefrontScope,
  SingleScope,
  HIPSingleThread,
  HIPWavefront,
  HIPWorkgroup,
  HIPAgent,
  HIPSystem,
  OpenCLWorkGroup,
  OpenCLDevice,
  OpenCLAllSVMDevices,
  OpenCLSubGroup,
  Last = OpenCLSubGroup
```

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


[clang] Add clang/docs/FunctionEffectAnalysis.rst. (PR #109855)

2024-09-26 Thread Doug Wyatt via cfe-commits


@@ -0,0 +1,503 @@
+
+Function Effect Analysis
+
+
+Introduction
+
+
+Clang Function Effect Analysis is a C++ language extension which can warn 
about "unsafe"
+constructs. The feature is currently tailored for the Performance Constraint 
attributes,
+``nonblocking`` and ``nonallocating``; functions with these attributes are 
verified as not
+containing any language constructs or calls to other functions which violate 
the constraint.
+(See :doc:`AttributeReference`.)
+
+
+The ``nonblocking`` and ``nonallocating`` attributes
+
+
+Attribute syntax
+
+
+The ``nonblocking`` and ``nonallocating`` attributes apply to function types, 
allowing them to be
+attached to functions, blocks, function pointers, lambdas, and member 
functions.
+
+.. code-block:: c++
+
+  // Functions
+  void nonblockingFunction() [[clang::nonblocking]];
+  void nonallocatingFunction() [[clang::nonallocating]];
+
+  // Function pointers
+  void (*nonblockingFunctionPtr)() [[clang::nonblocking]];
+
+  // Typedefs, type aliases.
+  typedef void (*NBFunctionPtrTypedef)() [[clang::nonblocking]];
+  using NBFunctionPtrTypeAlias_gnu = __attribute__((nonblocking)) void (*)();
+  using NBFunctionPtrTypeAlias_std = void (*)() [[clang::nonblocking]];
+
+  // C++ methods
+  struct Struct {
+void NBMethod() [[clang::nonblocking]];
+  };
+
+  // C++ lambdas
+  auto nbLambda = []() [[clang::nonblocking]] {};
+
+  // Blocks
+  void (^nbBlock)() = ^() [[clang::nonblocking]] {};
+
+The attribute applies only to the function itself. In particular, it does not 
apply to any nested
+functions or declarations, such as blocks, lambdas, and local classes.
+
+This document uses the C++/C23 syntax ``[[clang::nonblocking]]``, since it 
parallels the placement 
+of the ``noexcept`` specifier, and the attributes have other similarities to 
``noexcept``. The GNU
+``__attribute__((nonblocking))`` syntax is also supported. Note that it 
requires a different 
+placement on a C++ type alias.
+
+Like ``noexcept``, ``nonblocking`` and ``nonallocating`` have an optional 
argument, a compile-time
+constant boolean expression. By default, the argument is true, so 
``[[clang::nonblocking(true)]]``
+is equivalent to ``[[clang::nonblocking]]``, and declares the function type as 
never locking.
+
+
+Attribute semantics
+---
+
+Together with ``noexcept``, the ``nonallocating`` and ``nonblocking`` 
attributes define an ordered
+series of performance constraints. From weakest to strongest:
+
+- ``noexcept`` (as per the C++ standard): The function type will never throw 
an exception.
+- ``nonallocating``: The function type will never allocate memory on the heap, 
and never throw an
+  exception.
+- ``nonblocking``: The function type will never block on a lock, never 
allocate memory on the heap,
+  and never throw an exception.
+
+``nonblocking`` includes the ``nonallocating`` guarantee. 
+
+``nonblocking`` and ``nonallocating`` include the ``noexcept`` guarantee, but 
the presence of either
+attribute does not implicitly specify ``noexcept``. (It would be inappropriate 
for a Clang 
+attribute, ignored by non-Clang compilers, to imply a standard language 
feature.)
+
+``nonblocking(true)`` and ``nonallocating(true)`` apply to function *types*, 
and by extension, to
+function-like declarations. When applied to a declaration with a body, the 
compiler verifies the
+function, as described in the section "Analysis and warnings", below. 
Functions without an explicit
+performance constraint are not verified.
+
+``nonblocking(false)`` and ``nonallocating(false)`` are synonyms for the 
attributes ``blocking`` and
+``allocating``. They can be used on a function-like declaration to explicitly 
disable any potential
+inference of ``nonblocking`` or ``nonallocating`` during verification. 
(Inference is described later
+in this document). ``nonblocking(false)`` and ``nonallocating(false)`` are 
legal, but superfluous 
+when applied to a function *type*. ``float (int) [[nonblocking(false)]]`` and 
``float (int)`` are
+identical types.
+
+For all functions with no explicit performance constraint, the worst is 
assumed, that the function
+allocates memory and potentially blocks, unless it can be inferred otherwise, 
as described in the
+discussion of verification.
+
+The following list describes the meanings of all permutations of the two 
attributes and arguments:
+
+- ``nonblocking(true)`` + ``nonallocating(true)``: valid; 
``nonallocating(true)`` is superfluous but
+  does not contradict the guarantee.
+- ``nonblocking(true)`` + ``nonallocating(false)``: error, contradictory.
+- ``nonblocking(false)`` + ``nonallocating(true)``: valid; the function does 
not allocate memory,
+  but may lock for other reasons.
+- ``nonblocking(false)`` + ``nonallocating(false)``: valid.
+
+Type conversions
+
+
+A performance constrain

[clang] Add clang/docs/FunctionEffectAnalysis.rst. (PR #109855)

2024-09-26 Thread Doug Wyatt via cfe-commits

https://github.com/dougsonos updated 
https://github.com/llvm/llvm-project/pull/109855

>From 085965b324efde41168c5d51db3a368578d3458f Mon Sep 17 00:00:00 2001
From: Doug Wyatt 
Date: Mon, 23 Sep 2024 14:44:32 -0700
Subject: [PATCH 1/3] Add clang/docs/FunctionEffectAnalysis.rst.

---
 clang/docs/FunctionEffectAnalysis.rst | 503 ++
 clang/docs/index.rst  |   1 +
 2 files changed, 504 insertions(+)
 create mode 100644 clang/docs/FunctionEffectAnalysis.rst

diff --git a/clang/docs/FunctionEffectAnalysis.rst 
b/clang/docs/FunctionEffectAnalysis.rst
new file mode 100644
index 00..8c1cf8a483c5f1
--- /dev/null
+++ b/clang/docs/FunctionEffectAnalysis.rst
@@ -0,0 +1,503 @@
+
+Function Effect Analysis
+
+
+Introduction
+
+
+Clang Function Effect Analysis is a C++ language extension which can warn 
about "unsafe"
+constructs. The feature is currently tailored for the Performance Constraint 
attributes,
+``nonblocking`` and ``nonallocating``; functions with these attributes are 
verified as not
+containing any language constructs or calls to other functions which violate 
the constraint.
+(See :doc:`AttributeReference`.)
+
+
+The ``nonblocking`` and ``nonallocating`` attributes
+
+
+Attribute syntax
+
+
+The ``nonblocking`` and ``nonallocating`` attributes apply to function types, 
allowing them to be
+attached to functions, blocks, function pointers, lambdas, and member 
functions.
+
+.. code-block:: c++
+
+  // Functions
+  void nonblockingFunction() [[clang::nonblocking]];
+  void nonallocatingFunction() [[clang::nonallocating]];
+
+  // Function pointers
+  void (*nonblockingFunctionPtr)() [[clang::nonblocking]];
+
+  // Typedefs, type aliases.
+  typedef void (*NBFunctionPtrTypedef)() [[clang::nonblocking]];
+  using NBFunctionPtrTypeAlias_gnu = __attribute__((nonblocking)) void (*)();
+  using NBFunctionPtrTypeAlias_std = void (*)() [[clang::nonblocking]];
+
+  // C++ methods
+  struct Struct {
+void NBMethod() [[clang::nonblocking]];
+  };
+
+  // C++ lambdas
+  auto nbLambda = []() [[clang::nonblocking]] {};
+
+  // Blocks
+  void (^nbBlock)() = ^() [[clang::nonblocking]] {};
+
+The attribute applies only to the function itself. In particular, it does not 
apply to any nested
+functions or declarations, such as blocks, lambdas, and local classes.
+
+This document uses the C++/C23 syntax ``[[clang::nonblocking]]``, since it 
parallels the placement 
+of the ``noexcept`` specifier, and the attributes have other similarities to 
``noexcept``. The GNU
+``__attribute__((nonblocking))`` syntax is also supported. Note that it 
requires a different 
+placement on a C++ type alias.
+
+Like ``noexcept``, ``nonblocking`` and ``nonallocating`` have an optional 
argument, a compile-time
+constant boolean expression. By default, the argument is true, so 
``[[clang::nonblocking(true)]]``
+is equivalent to ``[[clang::nonblocking]]``, and declares the function type as 
never locking.
+
+
+Attribute semantics
+---
+
+Together with ``noexcept``, the ``nonallocating`` and ``nonblocking`` 
attributes define an ordered
+series of performance constraints. From weakest to strongest:
+
+- ``noexcept`` (as per the C++ standard): The function type will never throw 
an exception.
+- ``nonallocating``: The function type will never allocate memory on the heap, 
and never throw an
+  exception.
+- ``nonblocking``: The function type will never block on a lock, never 
allocate memory on the heap,
+  and never throw an exception.
+
+``nonblocking`` includes the ``nonallocating`` guarantee. 
+
+``nonblocking`` and ``nonallocating`` include the ``noexcept`` guarantee, but 
the presence of either
+attribute does not implicitly specify ``noexcept``. (It would be inappropriate 
for a Clang 
+attribute, ignored by non-Clang compilers, to imply a standard language 
feature.)
+
+``nonblocking(true)`` and ``nonallocating(true)`` apply to function *types*, 
and by extension, to
+function-like declarations. When applied to a declaration with a body, the 
compiler verifies the
+function, as described in the section "Analysis and warnings", below. 
Functions without an explicit
+performance constraint are not verified.
+
+``nonblocking(false)`` and ``nonallocating(false)`` are synonyms for the 
attributes ``blocking`` and
+``allocating``. They can be used on a function-like declaration to explicitly 
disable any potential
+inference of ``nonblocking`` or ``nonallocating`` during verification. 
(Inference is described later
+in this document). ``nonblocking(false)`` and ``nonallocating(false)`` are 
legal, but superfluous 
+when applied to a function *type*. ``float (int) [[nonblocking(false)]]`` and 
``float (int)`` are
+identical types.
+
+For all functions with no explicit performance constraint, the worst is 
assumed, that the function
+allocates memory an

[clang] Add clang/docs/FunctionEffectAnalysis.rst. (PR #109855)

2024-09-26 Thread Doug Wyatt via cfe-commits


@@ -0,0 +1,503 @@
+
+Function Effect Analysis
+
+
+Introduction
+
+
+Clang Function Effect Analysis is a C++ language extension which can warn 
about "unsafe"
+constructs. The feature is currently tailored for the Performance Constraint 
attributes,
+``nonblocking`` and ``nonallocating``; functions with these attributes are 
verified as not
+containing any language constructs or calls to other functions which violate 
the constraint.
+(See :doc:`AttributeReference`.)
+
+
+The ``nonblocking`` and ``nonallocating`` attributes
+
+
+Attribute syntax
+
+
+The ``nonblocking`` and ``nonallocating`` attributes apply to function types, 
allowing them to be
+attached to functions, blocks, function pointers, lambdas, and member 
functions.
+
+.. code-block:: c++
+
+  // Functions
+  void nonblockingFunction() [[clang::nonblocking]];
+  void nonallocatingFunction() [[clang::nonallocating]];
+
+  // Function pointers
+  void (*nonblockingFunctionPtr)() [[clang::nonblocking]];
+
+  // Typedefs, type aliases.
+  typedef void (*NBFunctionPtrTypedef)() [[clang::nonblocking]];
+  using NBFunctionPtrTypeAlias_gnu = __attribute__((nonblocking)) void (*)();
+  using NBFunctionPtrTypeAlias_std = void (*)() [[clang::nonblocking]];
+
+  // C++ methods
+  struct Struct {
+void NBMethod() [[clang::nonblocking]];
+  };
+
+  // C++ lambdas
+  auto nbLambda = []() [[clang::nonblocking]] {};
+
+  // Blocks
+  void (^nbBlock)() = ^() [[clang::nonblocking]] {};
+
+The attribute applies only to the function itself. In particular, it does not 
apply to any nested
+functions or declarations, such as blocks, lambdas, and local classes.
+
+This document uses the C++/C23 syntax ``[[clang::nonblocking]]``, since it 
parallels the placement 
+of the ``noexcept`` specifier, and the attributes have other similarities to 
``noexcept``. The GNU
+``__attribute__((nonblocking))`` syntax is also supported. Note that it 
requires a different 
+placement on a C++ type alias.
+
+Like ``noexcept``, ``nonblocking`` and ``nonallocating`` have an optional 
argument, a compile-time
+constant boolean expression. By default, the argument is true, so 
``[[clang::nonblocking(true)]]``
+is equivalent to ``[[clang::nonblocking]]``, and declares the function type as 
never locking.
+
+
+Attribute semantics
+---
+
+Together with ``noexcept``, the ``nonallocating`` and ``nonblocking`` 
attributes define an ordered
+series of performance constraints. From weakest to strongest:
+
+- ``noexcept`` (as per the C++ standard): The function type will never throw 
an exception.
+- ``nonallocating``: The function type will never allocate memory on the heap, 
and never throw an
+  exception.
+- ``nonblocking``: The function type will never block on a lock, never 
allocate memory on the heap,
+  and never throw an exception.
+
+``nonblocking`` includes the ``nonallocating`` guarantee. 
+
+``nonblocking`` and ``nonallocating`` include the ``noexcept`` guarantee, but 
the presence of either
+attribute does not implicitly specify ``noexcept``. (It would be inappropriate 
for a Clang 
+attribute, ignored by non-Clang compilers, to imply a standard language 
feature.)
+
+``nonblocking(true)`` and ``nonallocating(true)`` apply to function *types*, 
and by extension, to
+function-like declarations. When applied to a declaration with a body, the 
compiler verifies the
+function, as described in the section "Analysis and warnings", below. 
Functions without an explicit
+performance constraint are not verified.
+
+``nonblocking(false)`` and ``nonallocating(false)`` are synonyms for the 
attributes ``blocking`` and
+``allocating``. They can be used on a function-like declaration to explicitly 
disable any potential
+inference of ``nonblocking`` or ``nonallocating`` during verification. 
(Inference is described later
+in this document). ``nonblocking(false)`` and ``nonallocating(false)`` are 
legal, but superfluous 
+when applied to a function *type*. ``float (int) [[nonblocking(false)]]`` and 
``float (int)`` are
+identical types.
+
+For all functions with no explicit performance constraint, the worst is 
assumed, that the function
+allocates memory and potentially blocks, unless it can be inferred otherwise, 
as described in the
+discussion of verification.
+
+The following list describes the meanings of all permutations of the two 
attributes and arguments:
+
+- ``nonblocking(true)`` + ``nonallocating(true)``: valid; 
``nonallocating(true)`` is superfluous but
+  does not contradict the guarantee.
+- ``nonblocking(true)`` + ``nonallocating(false)``: error, contradictory.
+- ``nonblocking(false)`` + ``nonallocating(true)``: valid; the function does 
not allocate memory,
+  but may lock for other reasons.
+- ``nonblocking(false)`` + ``nonallocating(false)``: valid.
+
+Type conversions
+
+
+A performance constrain

[clang] Add clang/docs/FunctionEffectAnalysis.rst. (PR #109855)

2024-09-26 Thread Doug Wyatt via cfe-commits


@@ -0,0 +1,503 @@
+
+Function Effect Analysis
+
+
+Introduction
+
+
+Clang Function Effect Analysis is a C++ language extension which can warn 
about "unsafe"
+constructs. The feature is currently tailored for the Performance Constraint 
attributes,
+``nonblocking`` and ``nonallocating``; functions with these attributes are 
verified as not
+containing any language constructs or calls to other functions which violate 
the constraint.
+(See :doc:`AttributeReference`.)
+
+
+The ``nonblocking`` and ``nonallocating`` attributes
+
+
+Attribute syntax
+
+
+The ``nonblocking`` and ``nonallocating`` attributes apply to function types, 
allowing them to be
+attached to functions, blocks, function pointers, lambdas, and member 
functions.
+
+.. code-block:: c++
+
+  // Functions
+  void nonblockingFunction() [[clang::nonblocking]];
+  void nonallocatingFunction() [[clang::nonallocating]];
+
+  // Function pointers
+  void (*nonblockingFunctionPtr)() [[clang::nonblocking]];
+
+  // Typedefs, type aliases.
+  typedef void (*NBFunctionPtrTypedef)() [[clang::nonblocking]];
+  using NBFunctionPtrTypeAlias_gnu = __attribute__((nonblocking)) void (*)();
+  using NBFunctionPtrTypeAlias_std = void (*)() [[clang::nonblocking]];
+
+  // C++ methods
+  struct Struct {
+void NBMethod() [[clang::nonblocking]];
+  };
+
+  // C++ lambdas
+  auto nbLambda = []() [[clang::nonblocking]] {};
+
+  // Blocks
+  void (^nbBlock)() = ^() [[clang::nonblocking]] {};
+
+The attribute applies only to the function itself. In particular, it does not 
apply to any nested
+functions or declarations, such as blocks, lambdas, and local classes.
+
+This document uses the C++/C23 syntax ``[[clang::nonblocking]]``, since it 
parallels the placement 
+of the ``noexcept`` specifier, and the attributes have other similarities to 
``noexcept``. The GNU
+``__attribute__((nonblocking))`` syntax is also supported. Note that it 
requires a different 
+placement on a C++ type alias.
+
+Like ``noexcept``, ``nonblocking`` and ``nonallocating`` have an optional 
argument, a compile-time
+constant boolean expression. By default, the argument is true, so 
``[[clang::nonblocking(true)]]``
+is equivalent to ``[[clang::nonblocking]]``, and declares the function type as 
never locking.
+
+
+Attribute semantics
+---
+
+Together with ``noexcept``, the ``nonallocating`` and ``nonblocking`` 
attributes define an ordered
+series of performance constraints. From weakest to strongest:
+
+- ``noexcept`` (as per the C++ standard): The function type will never throw 
an exception.
+- ``nonallocating``: The function type will never allocate memory on the heap, 
and never throw an
+  exception.
+- ``nonblocking``: The function type will never block on a lock, never 
allocate memory on the heap,
+  and never throw an exception.
+
+``nonblocking`` includes the ``nonallocating`` guarantee. 
+
+``nonblocking`` and ``nonallocating`` include the ``noexcept`` guarantee, but 
the presence of either
+attribute does not implicitly specify ``noexcept``. (It would be inappropriate 
for a Clang 
+attribute, ignored by non-Clang compilers, to imply a standard language 
feature.)
+
+``nonblocking(true)`` and ``nonallocating(true)`` apply to function *types*, 
and by extension, to
+function-like declarations. When applied to a declaration with a body, the 
compiler verifies the
+function, as described in the section "Analysis and warnings", below. 
Functions without an explicit
+performance constraint are not verified.
+
+``nonblocking(false)`` and ``nonallocating(false)`` are synonyms for the 
attributes ``blocking`` and
+``allocating``. They can be used on a function-like declaration to explicitly 
disable any potential
+inference of ``nonblocking`` or ``nonallocating`` during verification. 
(Inference is described later
+in this document). ``nonblocking(false)`` and ``nonallocating(false)`` are 
legal, but superfluous 
+when applied to a function *type*. ``float (int) [[nonblocking(false)]]`` and 
``float (int)`` are
+identical types.
+
+For all functions with no explicit performance constraint, the worst is 
assumed, that the function
+allocates memory and potentially blocks, unless it can be inferred otherwise, 
as described in the
+discussion of verification.
+
+The following list describes the meanings of all permutations of the two 
attributes and arguments:
+
+- ``nonblocking(true)`` + ``nonallocating(true)``: valid; 
``nonallocating(true)`` is superfluous but
+  does not contradict the guarantee.
+- ``nonblocking(true)`` + ``nonallocating(false)``: error, contradictory.
+- ``nonblocking(false)`` + ``nonallocating(true)``: valid; the function does 
not allocate memory,
+  but may lock for other reasons.
+- ``nonblocking(false)`` + ``nonallocating(false)``: valid.
+
+Type conversions
+
+
+A performance constrain

[clang] Add clang/docs/FunctionEffectAnalysis.rst. (PR #109855)

2024-09-26 Thread Doug Wyatt via cfe-commits


@@ -0,0 +1,503 @@
+
+Function Effect Analysis
+
+
+Introduction
+
+
+Clang Function Effect Analysis is a C++ language extension which can warn 
about "unsafe"
+constructs. The feature is currently tailored for the Performance Constraint 
attributes,
+``nonblocking`` and ``nonallocating``; functions with these attributes are 
verified as not
+containing any language constructs or calls to other functions which violate 
the constraint.
+(See :doc:`AttributeReference`.)
+
+
+The ``nonblocking`` and ``nonallocating`` attributes
+
+
+Attribute syntax
+
+
+The ``nonblocking`` and ``nonallocating`` attributes apply to function types, 
allowing them to be
+attached to functions, blocks, function pointers, lambdas, and member 
functions.
+
+.. code-block:: c++
+
+  // Functions
+  void nonblockingFunction() [[clang::nonblocking]];
+  void nonallocatingFunction() [[clang::nonallocating]];
+
+  // Function pointers
+  void (*nonblockingFunctionPtr)() [[clang::nonblocking]];
+
+  // Typedefs, type aliases.
+  typedef void (*NBFunctionPtrTypedef)() [[clang::nonblocking]];
+  using NBFunctionPtrTypeAlias_gnu = __attribute__((nonblocking)) void (*)();
+  using NBFunctionPtrTypeAlias_std = void (*)() [[clang::nonblocking]];
+
+  // C++ methods
+  struct Struct {
+void NBMethod() [[clang::nonblocking]];
+  };
+
+  // C++ lambdas
+  auto nbLambda = []() [[clang::nonblocking]] {};
+
+  // Blocks
+  void (^nbBlock)() = ^() [[clang::nonblocking]] {};
+
+The attribute applies only to the function itself. In particular, it does not 
apply to any nested
+functions or declarations, such as blocks, lambdas, and local classes.
+
+This document uses the C++/C23 syntax ``[[clang::nonblocking]]``, since it 
parallels the placement 
+of the ``noexcept`` specifier, and the attributes have other similarities to 
``noexcept``. The GNU
+``__attribute__((nonblocking))`` syntax is also supported. Note that it 
requires a different 
+placement on a C++ type alias.
+
+Like ``noexcept``, ``nonblocking`` and ``nonallocating`` have an optional 
argument, a compile-time
+constant boolean expression. By default, the argument is true, so 
``[[clang::nonblocking(true)]]``
+is equivalent to ``[[clang::nonblocking]]``, and declares the function type as 
never locking.
+
+
+Attribute semantics
+---
+
+Together with ``noexcept``, the ``nonallocating`` and ``nonblocking`` 
attributes define an ordered
+series of performance constraints. From weakest to strongest:
+
+- ``noexcept`` (as per the C++ standard): The function type will never throw 
an exception.
+- ``nonallocating``: The function type will never allocate memory on the heap, 
and never throw an
+  exception.
+- ``nonblocking``: The function type will never block on a lock, never 
allocate memory on the heap,
+  and never throw an exception.
+
+``nonblocking`` includes the ``nonallocating`` guarantee. 
+
+``nonblocking`` and ``nonallocating`` include the ``noexcept`` guarantee, but 
the presence of either
+attribute does not implicitly specify ``noexcept``. (It would be inappropriate 
for a Clang 
+attribute, ignored by non-Clang compilers, to imply a standard language 
feature.)
+
+``nonblocking(true)`` and ``nonallocating(true)`` apply to function *types*, 
and by extension, to
+function-like declarations. When applied to a declaration with a body, the 
compiler verifies the
+function, as described in the section "Analysis and warnings", below. 
Functions without an explicit
+performance constraint are not verified.
+
+``nonblocking(false)`` and ``nonallocating(false)`` are synonyms for the 
attributes ``blocking`` and
+``allocating``. They can be used on a function-like declaration to explicitly 
disable any potential
+inference of ``nonblocking`` or ``nonallocating`` during verification. 
(Inference is described later
+in this document). ``nonblocking(false)`` and ``nonallocating(false)`` are 
legal, but superfluous 
+when applied to a function *type*. ``float (int) [[nonblocking(false)]]`` and 
``float (int)`` are
+identical types.
+
+For all functions with no explicit performance constraint, the worst is 
assumed, that the function
+allocates memory and potentially blocks, unless it can be inferred otherwise, 
as described in the
+discussion of verification.
+
+The following list describes the meanings of all permutations of the two 
attributes and arguments:
+
+- ``nonblocking(true)`` + ``nonallocating(true)``: valid; 
``nonallocating(true)`` is superfluous but
+  does not contradict the guarantee.
+- ``nonblocking(true)`` + ``nonallocating(false)``: error, contradictory.
+- ``nonblocking(false)`` + ``nonallocating(true)``: valid; the function does 
not allocate memory,
+  but may lock for other reasons.
+- ``nonblocking(false)`` + ``nonallocating(false)``: valid.
+
+Type conversions
+
+
+A performance constrain

[clang] Add clang/docs/FunctionEffectAnalysis.rst. (PR #109855)

2024-09-26 Thread Doug Wyatt via cfe-commits


@@ -0,0 +1,503 @@
+
+Function Effect Analysis
+
+
+Introduction
+
+
+Clang Function Effect Analysis is a C++ language extension which can warn 
about "unsafe"
+constructs. The feature is currently tailored for the Performance Constraint 
attributes,
+``nonblocking`` and ``nonallocating``; functions with these attributes are 
verified as not
+containing any language constructs or calls to other functions which violate 
the constraint.
+(See :doc:`AttributeReference`.)
+
+
+The ``nonblocking`` and ``nonallocating`` attributes
+
+
+Attribute syntax
+
+
+The ``nonblocking`` and ``nonallocating`` attributes apply to function types, 
allowing them to be
+attached to functions, blocks, function pointers, lambdas, and member 
functions.
+
+.. code-block:: c++
+
+  // Functions
+  void nonblockingFunction() [[clang::nonblocking]];
+  void nonallocatingFunction() [[clang::nonallocating]];
+
+  // Function pointers
+  void (*nonblockingFunctionPtr)() [[clang::nonblocking]];
+
+  // Typedefs, type aliases.
+  typedef void (*NBFunctionPtrTypedef)() [[clang::nonblocking]];
+  using NBFunctionPtrTypeAlias_gnu = __attribute__((nonblocking)) void (*)();
+  using NBFunctionPtrTypeAlias_std = void (*)() [[clang::nonblocking]];
+
+  // C++ methods
+  struct Struct {
+void NBMethod() [[clang::nonblocking]];
+  };
+
+  // C++ lambdas
+  auto nbLambda = []() [[clang::nonblocking]] {};
+
+  // Blocks
+  void (^nbBlock)() = ^() [[clang::nonblocking]] {};
+
+The attribute applies only to the function itself. In particular, it does not 
apply to any nested
+functions or declarations, such as blocks, lambdas, and local classes.
+
+This document uses the C++/C23 syntax ``[[clang::nonblocking]]``, since it 
parallels the placement 
+of the ``noexcept`` specifier, and the attributes have other similarities to 
``noexcept``. The GNU
+``__attribute__((nonblocking))`` syntax is also supported. Note that it 
requires a different 
+placement on a C++ type alias.
+
+Like ``noexcept``, ``nonblocking`` and ``nonallocating`` have an optional 
argument, a compile-time
+constant boolean expression. By default, the argument is true, so 
``[[clang::nonblocking(true)]]``
+is equivalent to ``[[clang::nonblocking]]``, and declares the function type as 
never locking.
+
+
+Attribute semantics
+---
+
+Together with ``noexcept``, the ``nonallocating`` and ``nonblocking`` 
attributes define an ordered
+series of performance constraints. From weakest to strongest:
+
+- ``noexcept`` (as per the C++ standard): The function type will never throw 
an exception.
+- ``nonallocating``: The function type will never allocate memory on the heap, 
and never throw an
+  exception.
+- ``nonblocking``: The function type will never block on a lock, never 
allocate memory on the heap,
+  and never throw an exception.
+
+``nonblocking`` includes the ``nonallocating`` guarantee. 
+
+``nonblocking`` and ``nonallocating`` include the ``noexcept`` guarantee, but 
the presence of either
+attribute does not implicitly specify ``noexcept``. (It would be inappropriate 
for a Clang 
+attribute, ignored by non-Clang compilers, to imply a standard language 
feature.)
+
+``nonblocking(true)`` and ``nonallocating(true)`` apply to function *types*, 
and by extension, to
+function-like declarations. When applied to a declaration with a body, the 
compiler verifies the
+function, as described in the section "Analysis and warnings", below. 
Functions without an explicit
+performance constraint are not verified.
+
+``nonblocking(false)`` and ``nonallocating(false)`` are synonyms for the 
attributes ``blocking`` and
+``allocating``. They can be used on a function-like declaration to explicitly 
disable any potential
+inference of ``nonblocking`` or ``nonallocating`` during verification. 
(Inference is described later
+in this document). ``nonblocking(false)`` and ``nonallocating(false)`` are 
legal, but superfluous 
+when applied to a function *type*. ``float (int) [[nonblocking(false)]]`` and 
``float (int)`` are
+identical types.
+
+For all functions with no explicit performance constraint, the worst is 
assumed, that the function
+allocates memory and potentially blocks, unless it can be inferred otherwise, 
as described in the
+discussion of verification.
+
+The following list describes the meanings of all permutations of the two 
attributes and arguments:
+
+- ``nonblocking(true)`` + ``nonallocating(true)``: valid; 
``nonallocating(true)`` is superfluous but
+  does not contradict the guarantee.
+- ``nonblocking(true)`` + ``nonallocating(false)``: error, contradictory.
+- ``nonblocking(false)`` + ``nonallocating(true)``: valid; the function does 
not allocate memory,
+  but may lock for other reasons.
+- ``nonblocking(false)`` + ``nonallocating(false)``: valid.
+
+Type conversions
+
+
+A performance constrain

[clang] Add clang/docs/FunctionEffectAnalysis.rst. (PR #109855)

2024-09-26 Thread Doug Wyatt via cfe-commits


@@ -0,0 +1,503 @@
+
+Function Effect Analysis
+
+
+Introduction
+
+
+Clang Function Effect Analysis is a C++ language extension which can warn 
about "unsafe"
+constructs. The feature is currently tailored for the Performance Constraint 
attributes,
+``nonblocking`` and ``nonallocating``; functions with these attributes are 
verified as not
+containing any language constructs or calls to other functions which violate 
the constraint.
+(See :doc:`AttributeReference`.)
+
+
+The ``nonblocking`` and ``nonallocating`` attributes
+
+
+Attribute syntax
+
+
+The ``nonblocking`` and ``nonallocating`` attributes apply to function types, 
allowing them to be
+attached to functions, blocks, function pointers, lambdas, and member 
functions.
+
+.. code-block:: c++
+
+  // Functions
+  void nonblockingFunction() [[clang::nonblocking]];
+  void nonallocatingFunction() [[clang::nonallocating]];
+
+  // Function pointers
+  void (*nonblockingFunctionPtr)() [[clang::nonblocking]];
+
+  // Typedefs, type aliases.
+  typedef void (*NBFunctionPtrTypedef)() [[clang::nonblocking]];
+  using NBFunctionPtrTypeAlias_gnu = __attribute__((nonblocking)) void (*)();
+  using NBFunctionPtrTypeAlias_std = void (*)() [[clang::nonblocking]];
+
+  // C++ methods
+  struct Struct {
+void NBMethod() [[clang::nonblocking]];
+  };
+
+  // C++ lambdas
+  auto nbLambda = []() [[clang::nonblocking]] {};
+
+  // Blocks
+  void (^nbBlock)() = ^() [[clang::nonblocking]] {};
+
+The attribute applies only to the function itself. In particular, it does not 
apply to any nested
+functions or declarations, such as blocks, lambdas, and local classes.
+
+This document uses the C++/C23 syntax ``[[clang::nonblocking]]``, since it 
parallels the placement 
+of the ``noexcept`` specifier, and the attributes have other similarities to 
``noexcept``. The GNU
+``__attribute__((nonblocking))`` syntax is also supported. Note that it 
requires a different 
+placement on a C++ type alias.
+
+Like ``noexcept``, ``nonblocking`` and ``nonallocating`` have an optional 
argument, a compile-time
+constant boolean expression. By default, the argument is true, so 
``[[clang::nonblocking(true)]]``
+is equivalent to ``[[clang::nonblocking]]``, and declares the function type as 
never locking.
+
+
+Attribute semantics
+---
+
+Together with ``noexcept``, the ``nonallocating`` and ``nonblocking`` 
attributes define an ordered
+series of performance constraints. From weakest to strongest:
+
+- ``noexcept`` (as per the C++ standard): The function type will never throw 
an exception.
+- ``nonallocating``: The function type will never allocate memory on the heap, 
and never throw an
+  exception.
+- ``nonblocking``: The function type will never block on a lock, never 
allocate memory on the heap,
+  and never throw an exception.
+
+``nonblocking`` includes the ``nonallocating`` guarantee. 
+
+``nonblocking`` and ``nonallocating`` include the ``noexcept`` guarantee, but 
the presence of either
+attribute does not implicitly specify ``noexcept``. (It would be inappropriate 
for a Clang 
+attribute, ignored by non-Clang compilers, to imply a standard language 
feature.)
+
+``nonblocking(true)`` and ``nonallocating(true)`` apply to function *types*, 
and by extension, to
+function-like declarations. When applied to a declaration with a body, the 
compiler verifies the
+function, as described in the section "Analysis and warnings", below. 
Functions without an explicit
+performance constraint are not verified.
+
+``nonblocking(false)`` and ``nonallocating(false)`` are synonyms for the 
attributes ``blocking`` and
+``allocating``. They can be used on a function-like declaration to explicitly 
disable any potential
+inference of ``nonblocking`` or ``nonallocating`` during verification. 
(Inference is described later
+in this document). ``nonblocking(false)`` and ``nonallocating(false)`` are 
legal, but superfluous 
+when applied to a function *type*. ``float (int) [[nonblocking(false)]]`` and 
``float (int)`` are
+identical types.
+
+For all functions with no explicit performance constraint, the worst is 
assumed, that the function
+allocates memory and potentially blocks, unless it can be inferred otherwise, 
as described in the
+discussion of verification.
+
+The following list describes the meanings of all permutations of the two 
attributes and arguments:
+
+- ``nonblocking(true)`` + ``nonallocating(true)``: valid; 
``nonallocating(true)`` is superfluous but
+  does not contradict the guarantee.
+- ``nonblocking(true)`` + ``nonallocating(false)``: error, contradictory.
+- ``nonblocking(false)`` + ``nonallocating(true)``: valid; the function does 
not allocate memory,
+  but may lock for other reasons.
+- ``nonblocking(false)`` + ``nonallocating(false)``: valid.
+
+Type conversions
+
+
+A performance constrain

[clang] Add clang/docs/FunctionEffectAnalysis.rst. (PR #109855)

2024-09-26 Thread Doug Wyatt via cfe-commits


@@ -0,0 +1,503 @@
+
+Function Effect Analysis
+
+
+Introduction
+
+
+Clang Function Effect Analysis is a C++ language extension which can warn 
about "unsafe"
+constructs. The feature is currently tailored for the Performance Constraint 
attributes,
+``nonblocking`` and ``nonallocating``; functions with these attributes are 
verified as not
+containing any language constructs or calls to other functions which violate 
the constraint.
+(See :doc:`AttributeReference`.)
+
+
+The ``nonblocking`` and ``nonallocating`` attributes
+
+
+Attribute syntax
+
+
+The ``nonblocking`` and ``nonallocating`` attributes apply to function types, 
allowing them to be
+attached to functions, blocks, function pointers, lambdas, and member 
functions.
+
+.. code-block:: c++
+
+  // Functions
+  void nonblockingFunction() [[clang::nonblocking]];
+  void nonallocatingFunction() [[clang::nonallocating]];
+
+  // Function pointers
+  void (*nonblockingFunctionPtr)() [[clang::nonblocking]];
+
+  // Typedefs, type aliases.
+  typedef void (*NBFunctionPtrTypedef)() [[clang::nonblocking]];
+  using NBFunctionPtrTypeAlias_gnu = __attribute__((nonblocking)) void (*)();
+  using NBFunctionPtrTypeAlias_std = void (*)() [[clang::nonblocking]];
+
+  // C++ methods
+  struct Struct {
+void NBMethod() [[clang::nonblocking]];
+  };
+
+  // C++ lambdas
+  auto nbLambda = []() [[clang::nonblocking]] {};
+
+  // Blocks
+  void (^nbBlock)() = ^() [[clang::nonblocking]] {};
+
+The attribute applies only to the function itself. In particular, it does not 
apply to any nested
+functions or declarations, such as blocks, lambdas, and local classes.
+
+This document uses the C++/C23 syntax ``[[clang::nonblocking]]``, since it 
parallels the placement 
+of the ``noexcept`` specifier, and the attributes have other similarities to 
``noexcept``. The GNU
+``__attribute__((nonblocking))`` syntax is also supported. Note that it 
requires a different 
+placement on a C++ type alias.
+
+Like ``noexcept``, ``nonblocking`` and ``nonallocating`` have an optional 
argument, a compile-time
+constant boolean expression. By default, the argument is true, so 
``[[clang::nonblocking(true)]]``
+is equivalent to ``[[clang::nonblocking]]``, and declares the function type as 
never locking.
+
+
+Attribute semantics
+---
+
+Together with ``noexcept``, the ``nonallocating`` and ``nonblocking`` 
attributes define an ordered
+series of performance constraints. From weakest to strongest:
+
+- ``noexcept`` (as per the C++ standard): The function type will never throw 
an exception.
+- ``nonallocating``: The function type will never allocate memory on the heap, 
and never throw an
+  exception.
+- ``nonblocking``: The function type will never block on a lock, never 
allocate memory on the heap,
+  and never throw an exception.
+
+``nonblocking`` includes the ``nonallocating`` guarantee. 
+
+``nonblocking`` and ``nonallocating`` include the ``noexcept`` guarantee, but 
the presence of either
+attribute does not implicitly specify ``noexcept``. (It would be inappropriate 
for a Clang 
+attribute, ignored by non-Clang compilers, to imply a standard language 
feature.)
+
+``nonblocking(true)`` and ``nonallocating(true)`` apply to function *types*, 
and by extension, to
+function-like declarations. When applied to a declaration with a body, the 
compiler verifies the
+function, as described in the section "Analysis and warnings", below. 
Functions without an explicit
+performance constraint are not verified.
+
+``nonblocking(false)`` and ``nonallocating(false)`` are synonyms for the 
attributes ``blocking`` and
+``allocating``. They can be used on a function-like declaration to explicitly 
disable any potential
+inference of ``nonblocking`` or ``nonallocating`` during verification. 
(Inference is described later
+in this document). ``nonblocking(false)`` and ``nonallocating(false)`` are 
legal, but superfluous 
+when applied to a function *type*. ``float (int) [[nonblocking(false)]]`` and 
``float (int)`` are
+identical types.
+
+For all functions with no explicit performance constraint, the worst is 
assumed, that the function
+allocates memory and potentially blocks, unless it can be inferred otherwise, 
as described in the
+discussion of verification.
+
+The following list describes the meanings of all permutations of the two 
attributes and arguments:
+
+- ``nonblocking(true)`` + ``nonallocating(true)``: valid; 
``nonallocating(true)`` is superfluous but
+  does not contradict the guarantee.
+- ``nonblocking(true)`` + ``nonallocating(false)``: error, contradictory.
+- ``nonblocking(false)`` + ``nonallocating(true)``: valid; the function does 
not allocate memory,
+  but may lock for other reasons.
+- ``nonblocking(false)`` + ``nonallocating(false)``: valid.

dougsonos wrote:

I had this as a table in the 

[clang] Add clang/docs/FunctionEffectAnalysis.rst. (PR #109855)

2024-09-26 Thread Doug Wyatt via cfe-commits


@@ -0,0 +1,503 @@
+
+Function Effect Analysis
+
+
+Introduction
+
+
+Clang Function Effect Analysis is a C++ language extension which can warn 
about "unsafe"
+constructs. The feature is currently tailored for the Performance Constraint 
attributes,
+``nonblocking`` and ``nonallocating``; functions with these attributes are 
verified as not
+containing any language constructs or calls to other functions which violate 
the constraint.
+(See :doc:`AttributeReference`.)
+
+
+The ``nonblocking`` and ``nonallocating`` attributes
+
+
+Attribute syntax
+
+
+The ``nonblocking`` and ``nonallocating`` attributes apply to function types, 
allowing them to be
+attached to functions, blocks, function pointers, lambdas, and member 
functions.
+
+.. code-block:: c++
+
+  // Functions
+  void nonblockingFunction() [[clang::nonblocking]];
+  void nonallocatingFunction() [[clang::nonallocating]];
+
+  // Function pointers
+  void (*nonblockingFunctionPtr)() [[clang::nonblocking]];
+
+  // Typedefs, type aliases.
+  typedef void (*NBFunctionPtrTypedef)() [[clang::nonblocking]];
+  using NBFunctionPtrTypeAlias_gnu = __attribute__((nonblocking)) void (*)();
+  using NBFunctionPtrTypeAlias_std = void (*)() [[clang::nonblocking]];
+
+  // C++ methods
+  struct Struct {
+void NBMethod() [[clang::nonblocking]];
+  };
+
+  // C++ lambdas
+  auto nbLambda = []() [[clang::nonblocking]] {};
+
+  // Blocks
+  void (^nbBlock)() = ^() [[clang::nonblocking]] {};
+
+The attribute applies only to the function itself. In particular, it does not 
apply to any nested
+functions or declarations, such as blocks, lambdas, and local classes.
+
+This document uses the C++/C23 syntax ``[[clang::nonblocking]]``, since it 
parallels the placement 
+of the ``noexcept`` specifier, and the attributes have other similarities to 
``noexcept``. The GNU
+``__attribute__((nonblocking))`` syntax is also supported. Note that it 
requires a different 
+placement on a C++ type alias.
+
+Like ``noexcept``, ``nonblocking`` and ``nonallocating`` have an optional 
argument, a compile-time
+constant boolean expression. By default, the argument is true, so 
``[[clang::nonblocking(true)]]``
+is equivalent to ``[[clang::nonblocking]]``, and declares the function type as 
never locking.
+
+
+Attribute semantics
+---
+
+Together with ``noexcept``, the ``nonallocating`` and ``nonblocking`` 
attributes define an ordered
+series of performance constraints. From weakest to strongest:
+
+- ``noexcept`` (as per the C++ standard): The function type will never throw 
an exception.
+- ``nonallocating``: The function type will never allocate memory on the heap, 
and never throw an
+  exception.
+- ``nonblocking``: The function type will never block on a lock, never 
allocate memory on the heap,
+  and never throw an exception.
+
+``nonblocking`` includes the ``nonallocating`` guarantee. 
+
+``nonblocking`` and ``nonallocating`` include the ``noexcept`` guarantee, but 
the presence of either
+attribute does not implicitly specify ``noexcept``. (It would be inappropriate 
for a Clang 
+attribute, ignored by non-Clang compilers, to imply a standard language 
feature.)
+
+``nonblocking(true)`` and ``nonallocating(true)`` apply to function *types*, 
and by extension, to
+function-like declarations. When applied to a declaration with a body, the 
compiler verifies the
+function, as described in the section "Analysis and warnings", below. 
Functions without an explicit
+performance constraint are not verified.
+
+``nonblocking(false)`` and ``nonallocating(false)`` are synonyms for the 
attributes ``blocking`` and
+``allocating``. They can be used on a function-like declaration to explicitly 
disable any potential
+inference of ``nonblocking`` or ``nonallocating`` during verification. 
(Inference is described later
+in this document). ``nonblocking(false)`` and ``nonallocating(false)`` are 
legal, but superfluous 
+when applied to a function *type*. ``float (int) [[nonblocking(false)]]`` and 
``float (int)`` are
+identical types.
+
+For all functions with no explicit performance constraint, the worst is 
assumed, that the function
+allocates memory and potentially blocks, unless it can be inferred otherwise, 
as described in the

dougsonos wrote:

@Sirraide, despite my being a former English major who dropped out to hack on 
computers and play in bands, I don't mind having my writing picked apart at 
all! Thanks :)

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


[clang] Add clang/docs/FunctionEffectAnalysis.rst. (PR #109855)

2024-09-26 Thread Doug Wyatt via cfe-commits

https://github.com/dougsonos updated 
https://github.com/llvm/llvm-project/pull/109855

>From 085965b324efde41168c5d51db3a368578d3458f Mon Sep 17 00:00:00 2001
From: Doug Wyatt 
Date: Mon, 23 Sep 2024 14:44:32 -0700
Subject: [PATCH 1/2] Add clang/docs/FunctionEffectAnalysis.rst.

---
 clang/docs/FunctionEffectAnalysis.rst | 503 ++
 clang/docs/index.rst  |   1 +
 2 files changed, 504 insertions(+)
 create mode 100644 clang/docs/FunctionEffectAnalysis.rst

diff --git a/clang/docs/FunctionEffectAnalysis.rst 
b/clang/docs/FunctionEffectAnalysis.rst
new file mode 100644
index 00..8c1cf8a483c5f1
--- /dev/null
+++ b/clang/docs/FunctionEffectAnalysis.rst
@@ -0,0 +1,503 @@
+
+Function Effect Analysis
+
+
+Introduction
+
+
+Clang Function Effect Analysis is a C++ language extension which can warn 
about "unsafe"
+constructs. The feature is currently tailored for the Performance Constraint 
attributes,
+``nonblocking`` and ``nonallocating``; functions with these attributes are 
verified as not
+containing any language constructs or calls to other functions which violate 
the constraint.
+(See :doc:`AttributeReference`.)
+
+
+The ``nonblocking`` and ``nonallocating`` attributes
+
+
+Attribute syntax
+
+
+The ``nonblocking`` and ``nonallocating`` attributes apply to function types, 
allowing them to be
+attached to functions, blocks, function pointers, lambdas, and member 
functions.
+
+.. code-block:: c++
+
+  // Functions
+  void nonblockingFunction() [[clang::nonblocking]];
+  void nonallocatingFunction() [[clang::nonallocating]];
+
+  // Function pointers
+  void (*nonblockingFunctionPtr)() [[clang::nonblocking]];
+
+  // Typedefs, type aliases.
+  typedef void (*NBFunctionPtrTypedef)() [[clang::nonblocking]];
+  using NBFunctionPtrTypeAlias_gnu = __attribute__((nonblocking)) void (*)();
+  using NBFunctionPtrTypeAlias_std = void (*)() [[clang::nonblocking]];
+
+  // C++ methods
+  struct Struct {
+void NBMethod() [[clang::nonblocking]];
+  };
+
+  // C++ lambdas
+  auto nbLambda = []() [[clang::nonblocking]] {};
+
+  // Blocks
+  void (^nbBlock)() = ^() [[clang::nonblocking]] {};
+
+The attribute applies only to the function itself. In particular, it does not 
apply to any nested
+functions or declarations, such as blocks, lambdas, and local classes.
+
+This document uses the C++/C23 syntax ``[[clang::nonblocking]]``, since it 
parallels the placement 
+of the ``noexcept`` specifier, and the attributes have other similarities to 
``noexcept``. The GNU
+``__attribute__((nonblocking))`` syntax is also supported. Note that it 
requires a different 
+placement on a C++ type alias.
+
+Like ``noexcept``, ``nonblocking`` and ``nonallocating`` have an optional 
argument, a compile-time
+constant boolean expression. By default, the argument is true, so 
``[[clang::nonblocking(true)]]``
+is equivalent to ``[[clang::nonblocking]]``, and declares the function type as 
never locking.
+
+
+Attribute semantics
+---
+
+Together with ``noexcept``, the ``nonallocating`` and ``nonblocking`` 
attributes define an ordered
+series of performance constraints. From weakest to strongest:
+
+- ``noexcept`` (as per the C++ standard): The function type will never throw 
an exception.
+- ``nonallocating``: The function type will never allocate memory on the heap, 
and never throw an
+  exception.
+- ``nonblocking``: The function type will never block on a lock, never 
allocate memory on the heap,
+  and never throw an exception.
+
+``nonblocking`` includes the ``nonallocating`` guarantee. 
+
+``nonblocking`` and ``nonallocating`` include the ``noexcept`` guarantee, but 
the presence of either
+attribute does not implicitly specify ``noexcept``. (It would be inappropriate 
for a Clang 
+attribute, ignored by non-Clang compilers, to imply a standard language 
feature.)
+
+``nonblocking(true)`` and ``nonallocating(true)`` apply to function *types*, 
and by extension, to
+function-like declarations. When applied to a declaration with a body, the 
compiler verifies the
+function, as described in the section "Analysis and warnings", below. 
Functions without an explicit
+performance constraint are not verified.
+
+``nonblocking(false)`` and ``nonallocating(false)`` are synonyms for the 
attributes ``blocking`` and
+``allocating``. They can be used on a function-like declaration to explicitly 
disable any potential
+inference of ``nonblocking`` or ``nonallocating`` during verification. 
(Inference is described later
+in this document). ``nonblocking(false)`` and ``nonallocating(false)`` are 
legal, but superfluous 
+when applied to a function *type*. ``float (int) [[nonblocking(false)]]`` and 
``float (int)`` are
+identical types.
+
+For all functions with no explicit performance constraint, the worst is 
assumed, that the function
+allocates memory an

[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

2024-09-26 Thread Doug Wyatt via cfe-commits

https://github.com/dougsonos edited 
https://github.com/llvm/llvm-project/pull/99656
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

2024-09-26 Thread Doug Wyatt via cfe-commits

https://github.com/dougsonos deleted 
https://github.com/llvm/llvm-project/pull/99656
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

2024-09-26 Thread Doug Wyatt via cfe-commits


@@ -4712,12 +4715,13 @@ class FunctionEffect {
 public:
   /// Identifies the particular effect.
   enum class Kind : uint8_t {
-None = 0,
-NonBlocking = 1,
-NonAllocating = 2,
-Blocking = 3,
-Allocating = 4
+NonBlocking = 0,
+NonAllocating = 1,
+Blocking = 2,
+Allocating = 3,
+

dougsonos wrote:

Although ... clangd is telling me that adding a value to the enum creates a 
bunch of "missing switch case" warnings. Hm.

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


[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

2024-09-26 Thread Doug Wyatt via cfe-commits


@@ -8413,6 +8418,15 @@ void ASTReader::InitializeSema(Sema &S) {
 NewOverrides.applyOverrides(SemaObj->getLangOpts());
   }
 
+  for (GlobalDeclID ID : DeclsWithEffectsToVerify) {
+Decl *D = GetDecl(ID);
+if (auto *FD = dyn_cast(D))
+  SemaObj->addDeclWithEffects(FD, FD->getFunctionEffects());
+else if (auto *BD = dyn_cast(D))
+  SemaObj->addDeclWithEffects(BD, BD->getFunctionEffects());
+  }

dougsonos wrote:

I'll make it `llvm_unreachable` (according to its docs:
```
/// Use this instead of assert(0). It conveys intent more clearly, suppresses
/// diagnostics for unreachable code paths, and allows compilers to omit
/// unnecessary code.
```

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


[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

2024-09-26 Thread Doug Wyatt via cfe-commits

https://github.com/dougsonos edited 
https://github.com/llvm/llvm-project/pull/99656
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

2024-09-26 Thread Doug Wyatt via cfe-commits


@@ -4712,12 +4715,13 @@ class FunctionEffect {
 public:
   /// Identifies the particular effect.
   enum class Kind : uint8_t {
-None = 0,
-NonBlocking = 1,
-NonAllocating = 2,
-Blocking = 3,
-Allocating = 4
+NonBlocking = 0,
+NonAllocating = 1,
+Blocking = 2,
+Allocating = 3,
+

dougsonos wrote:

Sure. Being explicit with enum values is an ancient habit from the days when 
debugging sometimes involved looking at memory in hex. I'd agree they're 
superfluous here.

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


[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

2024-09-25 Thread Doug Wyatt via cfe-commits

https://github.com/dougsonos edited 
https://github.com/llvm/llvm-project/pull/99656
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

2024-09-25 Thread Doug Wyatt via cfe-commits


@@ -0,0 +1,1572 @@
+//=== SemaFunctionEffects.cpp - Sema handling of function effects 
-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+//
+// This file implements Sema handling of function effects.
+//
+//===--===//
+
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/Stmt.h"
+#include "clang/AST/Type.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Sema/SemaInternal.h"
+
+#define DEBUG_TYPE "effectanalysis"
+
+using namespace clang;
+
+namespace {
+
+enum class ViolationID : uint8_t {
+  None = 0, // Sentinel for an empty Violation.
+  // These first few map to a %select{} in a diagnostic.
+  BaseDiagnosticIndex,
+  AllocatesMemory = BaseDiagnosticIndex,
+  ThrowsOrCatchesExceptions,
+  HasStaticLocalVariable,
+  AccessesThreadLocalVariable,
+  AccessesObjCMethodOrProperty,
+
+  // These only apply to callees, where the analysis stops at the Decl.
+  DeclDisallowsInference,
+
+  // These both apply to indirect calls. The difference is that sometimes
+  // we have an actual Decl (generally a variable) which is the function
+  // pointer being called, and sometimes, typically due to a cast, we only
+  // have an expression.
+  CallsDeclWithoutEffect,
+  CallsExprWithoutEffect,
+};
+
+// Information about the AST context in which a violation was found, so
+// that diagnostics can point to the correct source.
+class ViolationSite {
+public:
+  enum class Kind : uint8_t {
+Default = 0, // Function body.
+MemberInitializer = 1,
+DefaultArgExpr = 2
+  };
+
+private:
+  llvm::PointerIntPair Impl;
+
+public:
+  ViolationSite() = default;
+
+  explicit ViolationSite(CXXDefaultArgExpr *E)
+  : Impl(E, Kind::DefaultArgExpr) {}
+
+  Kind kind() const { return static_cast(Impl.getInt()); }
+  CXXDefaultArgExpr *defaultArgExpr() const { return Impl.getPointer(); }
+
+  void setKind(Kind K) { Impl.setPointerAndInt(nullptr, K); }
+};
+
+// Represents a violation of the rules, potentially for the entire duration of
+// the analysis phase, in order to refer to it when explaining why a caller has
+// been made unsafe by a callee. Can be transformed into either a Diagnostic
+// (warning or a note), depending on whether the violation pertains to a
+// function failing to be verifed as holding an effect vs. a function failing 
to
+// be inferred as holding that effect.
+struct Violation {
+  FunctionEffect Effect;
+  FunctionEffect
+  CalleeEffectPreventingInference; // Only for certain IDs; can be None.
+  ViolationID ID = ViolationID::None;
+  ViolationSite Site;
+  SourceLocation Loc;
+  const Decl *Callee = nullptr; // Only valid for Calls*.
+
+  Violation() = default;
+
+  Violation(FunctionEffect Effect, ViolationID ID, ViolationSite VS,
+SourceLocation Loc, const Decl *Callee = nullptr,
+std::optional CalleeEffect = std::nullopt)
+  : Effect(Effect), CalleeEffectPreventingInference(
+CalleeEffect.value_or(FunctionEffect())),
+ID(ID), Site(VS), Loc(Loc), Callee(Callee) {}
+
+  unsigned diagnosticSelectIndex() const {
+return unsigned(ID) - unsigned(ViolationID::BaseDiagnosticIndex);
+  }
+};
+
+enum class SpecialFuncType : uint8_t { None, OperatorNew, OperatorDelete };
+enum class CallableType : uint8_t {
+  // Unknown: probably function pointer.
+  Unknown,
+  Function,
+  Virtual,
+  Block
+};
+
+// Return whether a function's effects CAN be verified.
+// The question of whether it SHOULD be verified is independent.
+static bool functionIsVerifiable(const FunctionDecl *FD) {
+  if (FD->isTrivial()) {
+// Otherwise `struct x { int a; };` would have an unverifiable default
+// constructor.
+return true;
+  }
+  return FD->hasBody();
+}
+
+static bool isNoexcept(const FunctionDecl *FD) {
+  const auto *FPT = FD->getType()->castAs();

dougsonos wrote:

Ah gotcha. I looked through all the `noreturn` builtins and most seem like they 
do trap/abort/exit. I don't know if a typical implementation `longjmp()` really 
does allocate anything on the heap or block, but philosophically I agree that 
it's like throwing an exception, which does allocate.

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


[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

2024-09-25 Thread Doug Wyatt via cfe-commits


@@ -4719,8 +4719,9 @@ class FunctionEffect {
 NonBlocking = 1,

dougsonos wrote:

Good idea. I was afraid of a lot of fallout but it was actually quite an easy 
change to get rid of the sentinel value.

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


[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

2024-09-25 Thread Doug Wyatt via cfe-commits


@@ -0,0 +1,1572 @@
+//=== SemaFunctionEffects.cpp - Sema handling of function effects 
-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+//
+// This file implements Sema handling of function effects.
+//
+//===--===//
+
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/Stmt.h"
+#include "clang/AST/Type.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Sema/SemaInternal.h"
+
+#define DEBUG_TYPE "effectanalysis"
+
+using namespace clang;
+
+namespace {
+
+enum class ViolationID : uint8_t {
+  None = 0, // Sentinel for an empty Violation.
+  // These first few map to a %select{} in a diagnostic.
+  BaseDiagnosticIndex,
+  AllocatesMemory = BaseDiagnosticIndex,
+  ThrowsOrCatchesExceptions,
+  HasStaticLocalVariable,
+  AccessesThreadLocalVariable,
+  AccessesObjCMethodOrProperty,
+
+  // These only apply to callees, where the analysis stops at the Decl.
+  DeclDisallowsInference,
+
+  // These both apply to indirect calls. The difference is that sometimes
+  // we have an actual Decl (generally a variable) which is the function
+  // pointer being called, and sometimes, typically due to a cast, we only
+  // have an expression.
+  CallsDeclWithoutEffect,
+  CallsExprWithoutEffect,
+};
+
+// Information about the AST context in which a violation was found, so
+// that diagnostics can point to the correct source.
+class ViolationSite {
+public:
+  enum class Kind : uint8_t {
+Default = 0, // Function body.
+MemberInitializer = 1,
+DefaultArgExpr = 2
+  };
+
+private:
+  llvm::PointerIntPair Impl;
+
+public:
+  ViolationSite() = default;
+
+  explicit ViolationSite(CXXDefaultArgExpr *E)
+  : Impl(E, Kind::DefaultArgExpr) {}
+
+  Kind kind() const { return static_cast(Impl.getInt()); }
+  CXXDefaultArgExpr *defaultArgExpr() const { return Impl.getPointer(); }
+
+  void setKind(Kind K) { Impl.setPointerAndInt(nullptr, K); }
+};
+
+// Represents a violation of the rules, potentially for the entire duration of
+// the analysis phase, in order to refer to it when explaining why a caller has
+// been made unsafe by a callee. Can be transformed into either a Diagnostic
+// (warning or a note), depending on whether the violation pertains to a
+// function failing to be verifed as holding an effect vs. a function failing 
to
+// be inferred as holding that effect.
+struct Violation {
+  FunctionEffect Effect;
+  FunctionEffect
+  CalleeEffectPreventingInference; // Only for certain IDs; can be None.
+  ViolationID ID = ViolationID::None;
+  ViolationSite Site;
+  SourceLocation Loc;
+  const Decl *Callee = nullptr; // Only valid for Calls*.
+
+  Violation() = default;
+
+  Violation(FunctionEffect Effect, ViolationID ID, ViolationSite VS,
+SourceLocation Loc, const Decl *Callee = nullptr,
+std::optional CalleeEffect = std::nullopt)
+  : Effect(Effect), CalleeEffectPreventingInference(
+CalleeEffect.value_or(FunctionEffect())),
+ID(ID), Site(VS), Loc(Loc), Callee(Callee) {}
+
+  unsigned diagnosticSelectIndex() const {
+return unsigned(ID) - unsigned(ViolationID::BaseDiagnosticIndex);
+  }
+};
+
+enum class SpecialFuncType : uint8_t { None, OperatorNew, OperatorDelete };
+enum class CallableType : uint8_t {
+  // Unknown: probably function pointer.
+  Unknown,
+  Function,
+  Virtual,
+  Block
+};
+
+// Return whether a function's effects CAN be verified.
+// The question of whether it SHOULD be verified is independent.
+static bool functionIsVerifiable(const FunctionDecl *FD) {
+  if (FD->isTrivial()) {
+// Otherwise `struct x { int a; };` would have an unverifiable default
+// constructor.
+return true;
+  }
+  return FD->hasBody();
+}
+
+static bool isNoexcept(const FunctionDecl *FD) {
+  const auto *FPT = FD->getType()->castAs();

dougsonos wrote:

I hope I haven't misunderstood you here. I did add a test involving a 
`noreturn` function in attr-nonblocking-constraints.c (distilled):

```
// Has no prototype, noreturn.
[[noreturn]]
void aborts();

void nb2(void) __attribute__((nonblocking)) {
  aborts(); // no diagnostic because it's noreturn.
}
```

The idea is that abort/terminate is a "force majeure", outside the scope of the 
effect analysis.

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


[clang] Add clang/docs/FunctionEffectAnalysis.rst. (PR #109855)

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

https://github.com/dougsonos created 
https://github.com/llvm/llvm-project/pull/109855

Follow-on from #99656, which introduces 2nd pass caller/callee analysis for 
function effects.

Wrote a new documentation page, derived directly from the RFC posted to LLVM 
Discourse earlier this year.

>From 085965b324efde41168c5d51db3a368578d3458f Mon Sep 17 00:00:00 2001
From: Doug Wyatt 
Date: Mon, 23 Sep 2024 14:44:32 -0700
Subject: [PATCH] Add clang/docs/FunctionEffectAnalysis.rst.

---
 clang/docs/FunctionEffectAnalysis.rst | 503 ++
 clang/docs/index.rst  |   1 +
 2 files changed, 504 insertions(+)
 create mode 100644 clang/docs/FunctionEffectAnalysis.rst

diff --git a/clang/docs/FunctionEffectAnalysis.rst 
b/clang/docs/FunctionEffectAnalysis.rst
new file mode 100644
index 00..8c1cf8a483c5f1
--- /dev/null
+++ b/clang/docs/FunctionEffectAnalysis.rst
@@ -0,0 +1,503 @@
+
+Function Effect Analysis
+
+
+Introduction
+
+
+Clang Function Effect Analysis is a C++ language extension which can warn 
about "unsafe"
+constructs. The feature is currently tailored for the Performance Constraint 
attributes,
+``nonblocking`` and ``nonallocating``; functions with these attributes are 
verified as not
+containing any language constructs or calls to other functions which violate 
the constraint.
+(See :doc:`AttributeReference`.)
+
+
+The ``nonblocking`` and ``nonallocating`` attributes
+
+
+Attribute syntax
+
+
+The ``nonblocking`` and ``nonallocating`` attributes apply to function types, 
allowing them to be
+attached to functions, blocks, function pointers, lambdas, and member 
functions.
+
+.. code-block:: c++
+
+  // Functions
+  void nonblockingFunction() [[clang::nonblocking]];
+  void nonallocatingFunction() [[clang::nonallocating]];
+
+  // Function pointers
+  void (*nonblockingFunctionPtr)() [[clang::nonblocking]];
+
+  // Typedefs, type aliases.
+  typedef void (*NBFunctionPtrTypedef)() [[clang::nonblocking]];
+  using NBFunctionPtrTypeAlias_gnu = __attribute__((nonblocking)) void (*)();
+  using NBFunctionPtrTypeAlias_std = void (*)() [[clang::nonblocking]];
+
+  // C++ methods
+  struct Struct {
+void NBMethod() [[clang::nonblocking]];
+  };
+
+  // C++ lambdas
+  auto nbLambda = []() [[clang::nonblocking]] {};
+
+  // Blocks
+  void (^nbBlock)() = ^() [[clang::nonblocking]] {};
+
+The attribute applies only to the function itself. In particular, it does not 
apply to any nested
+functions or declarations, such as blocks, lambdas, and local classes.
+
+This document uses the C++/C23 syntax ``[[clang::nonblocking]]``, since it 
parallels the placement 
+of the ``noexcept`` specifier, and the attributes have other similarities to 
``noexcept``. The GNU
+``__attribute__((nonblocking))`` syntax is also supported. Note that it 
requires a different 
+placement on a C++ type alias.
+
+Like ``noexcept``, ``nonblocking`` and ``nonallocating`` have an optional 
argument, a compile-time
+constant boolean expression. By default, the argument is true, so 
``[[clang::nonblocking(true)]]``
+is equivalent to ``[[clang::nonblocking]]``, and declares the function type as 
never locking.
+
+
+Attribute semantics
+---
+
+Together with ``noexcept``, the ``nonallocating`` and ``nonblocking`` 
attributes define an ordered
+series of performance constraints. From weakest to strongest:
+
+- ``noexcept`` (as per the C++ standard): The function type will never throw 
an exception.
+- ``nonallocating``: The function type will never allocate memory on the heap, 
and never throw an
+  exception.
+- ``nonblocking``: The function type will never block on a lock, never 
allocate memory on the heap,
+  and never throw an exception.
+
+``nonblocking`` includes the ``nonallocating`` guarantee. 
+
+``nonblocking`` and ``nonallocating`` include the ``noexcept`` guarantee, but 
the presence of either
+attribute does not implicitly specify ``noexcept``. (It would be inappropriate 
for a Clang 
+attribute, ignored by non-Clang compilers, to imply a standard language 
feature.)
+
+``nonblocking(true)`` and ``nonallocating(true)`` apply to function *types*, 
and by extension, to
+function-like declarations. When applied to a declaration with a body, the 
compiler verifies the
+function, as described in the section "Analysis and warnings", below. 
Functions without an explicit
+performance constraint are not verified.
+
+``nonblocking(false)`` and ``nonallocating(false)`` are synonyms for the 
attributes ``blocking`` and
+``allocating``. They can be used on a function-like declaration to explicitly 
disable any potential
+inference of ``nonblocking`` or ``nonallocating`` during verification. 
(Inference is described later
+in this document). ``nonblocking(false)`` and ``nonallocating(false)`` are 
legal, but superfluous 
+when applied to a function *type*. ``flo

[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

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


@@ -1128,12 +1128,18 @@ def ThreadSafetyBeta : DiagGroup<"thread-safety-beta">;
 // Uniqueness Analysis warnings
 def Consumed   : DiagGroup<"consumed">;
 
+// Warnings and notes related to the function effects system underlying
+// the nonblocking and nonallocating attributes.
+def FunctionEffects : DiagGroup<"function-effects">;
+def PerfConstraintImpliesNoexcept : 
DiagGroup<"perf-constraint-implies-noexcept">;

dougsonos wrote:

My first impulse was to tie them together, but to reason through this...

The argument for not having `-Wfunction-effects` enabled by default is that if 
I get a new version of a library which has adopted the attributes, I shouldn't 
immediately get warnings everywhere I use the library.

But I won't get `-Wperf-constraint-implies-noexcept` warnings anywhere until I 
start adding `[[nonblocking]]` or `[[nonallocating]]` in my own code, so it's 
reasonable to include that in `-Wall`.

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


[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

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


@@ -0,0 +1,1572 @@
+//=== SemaFunctionEffects.cpp - Sema handling of function effects 
-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+//
+// This file implements Sema handling of function effects.
+//
+//===--===//
+
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/Stmt.h"
+#include "clang/AST/Type.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Sema/SemaInternal.h"
+
+#define DEBUG_TYPE "effectanalysis"
+
+using namespace clang;
+
+namespace {
+
+enum class ViolationID : uint8_t {
+  None = 0, // Sentinel for an empty Violation.
+  // These first few map to a %select{} in a diagnostic.
+  BaseDiagnosticIndex,
+  AllocatesMemory = BaseDiagnosticIndex,
+  ThrowsOrCatchesExceptions,
+  HasStaticLocalVariable,
+  AccessesThreadLocalVariable,
+  AccessesObjCMethodOrProperty,
+
+  // These only apply to callees, where the analysis stops at the Decl.
+  DeclDisallowsInference,
+
+  // These both apply to indirect calls. The difference is that sometimes
+  // we have an actual Decl (generally a variable) which is the function
+  // pointer being called, and sometimes, typically due to a cast, we only
+  // have an expression.
+  CallsDeclWithoutEffect,
+  CallsExprWithoutEffect,
+};
+
+// Information about the AST context in which a violation was found, so
+// that diagnostics can point to the correct source.
+class ViolationSite {
+public:
+  enum class Kind : uint8_t {
+Default = 0, // Function body.
+MemberInitializer = 1,
+DefaultArgExpr = 2
+  };
+
+private:
+  llvm::PointerIntPair Impl;
+
+public:
+  ViolationSite() = default;
+
+  explicit ViolationSite(CXXDefaultArgExpr *E)
+  : Impl(E, Kind::DefaultArgExpr) {}
+
+  Kind kind() const { return static_cast(Impl.getInt()); }
+  CXXDefaultArgExpr *defaultArgExpr() const { return Impl.getPointer(); }
+
+  void setKind(Kind K) { Impl.setPointerAndInt(nullptr, K); }
+};
+
+// Represents a violation of the rules, potentially for the entire duration of
+// the analysis phase, in order to refer to it when explaining why a caller has
+// been made unsafe by a callee. Can be transformed into either a Diagnostic
+// (warning or a note), depending on whether the violation pertains to a
+// function failing to be verifed as holding an effect vs. a function failing 
to
+// be inferred as holding that effect.
+struct Violation {
+  FunctionEffect Effect;
+  FunctionEffect
+  CalleeEffectPreventingInference; // Only for certain IDs; can be None.
+  ViolationID ID = ViolationID::None;
+  ViolationSite Site;
+  SourceLocation Loc;
+  const Decl *Callee = nullptr; // Only valid for Calls*.

dougsonos wrote:

Made the comment very much more specific

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


[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

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


@@ -1,6 +1,8 @@
 // RUN: %clang_cc1 -fsyntax-only -fblocks -fcxx-exceptions -verify %s
 // RUN: %clang_cc1 -fsyntax-only -fblocks -verify -x c -std=c23 %s
 
+#pragma clang diagnostic warning "-Wfunction-effects"

dougsonos wrote:

Moved

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


[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

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


@@ -0,0 +1,1572 @@
+//=== SemaFunctionEffects.cpp - Sema handling of function effects 
-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+//
+// This file implements Sema handling of function effects.
+//
+//===--===//
+
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/Stmt.h"
+#include "clang/AST/Type.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Sema/SemaInternal.h"
+
+#define DEBUG_TYPE "effectanalysis"
+
+using namespace clang;
+
+namespace {
+
+enum class ViolationID : uint8_t {
+  None = 0, // Sentinel for an empty Violation.
+  // These first few map to a %select{} in a diagnostic.
+  BaseDiagnosticIndex,
+  AllocatesMemory = BaseDiagnosticIndex,
+  ThrowsOrCatchesExceptions,
+  HasStaticLocalVariable,
+  AccessesThreadLocalVariable,
+  AccessesObjCMethodOrProperty,
+
+  // These only apply to callees, where the analysis stops at the Decl.
+  DeclDisallowsInference,
+
+  // These both apply to indirect calls. The difference is that sometimes
+  // we have an actual Decl (generally a variable) which is the function
+  // pointer being called, and sometimes, typically due to a cast, we only
+  // have an expression.
+  CallsDeclWithoutEffect,
+  CallsExprWithoutEffect,
+};
+
+// Information about the AST context in which a violation was found, so
+// that diagnostics can point to the correct source.
+class ViolationSite {
+public:
+  enum class Kind : uint8_t {
+Default = 0, // Function body.
+MemberInitializer = 1,
+DefaultArgExpr = 2
+  };
+
+private:
+  llvm::PointerIntPair Impl;
+
+public:
+  ViolationSite() = default;
+
+  explicit ViolationSite(CXXDefaultArgExpr *E)
+  : Impl(E, Kind::DefaultArgExpr) {}
+
+  Kind kind() const { return static_cast(Impl.getInt()); }
+  CXXDefaultArgExpr *defaultArgExpr() const { return Impl.getPointer(); }
+
+  void setKind(Kind K) { Impl.setPointerAndInt(nullptr, K); }
+};
+
+// Represents a violation of the rules, potentially for the entire duration of
+// the analysis phase, in order to refer to it when explaining why a caller has
+// been made unsafe by a callee. Can be transformed into either a Diagnostic
+// (warning or a note), depending on whether the violation pertains to a
+// function failing to be verifed as holding an effect vs. a function failing 
to
+// be inferred as holding that effect.
+struct Violation {
+  FunctionEffect Effect;
+  FunctionEffect
+  CalleeEffectPreventingInference; // Only for certain IDs; can be None.
+  ViolationID ID = ViolationID::None;
+  ViolationSite Site;
+  SourceLocation Loc;
+  const Decl *Callee = nullptr; // Only valid for Calls*.
+
+  Violation() = default;
+
+  Violation(FunctionEffect Effect, ViolationID ID, ViolationSite VS,
+SourceLocation Loc, const Decl *Callee = nullptr,
+std::optional CalleeEffect = std::nullopt)
+  : Effect(Effect), CalleeEffectPreventingInference(
+CalleeEffect.value_or(FunctionEffect())),
+ID(ID), Site(VS), Loc(Loc), Callee(Callee) {}
+
+  unsigned diagnosticSelectIndex() const {
+return unsigned(ID) - unsigned(ViolationID::BaseDiagnosticIndex);
+  }
+};
+
+enum class SpecialFuncType : uint8_t { None, OperatorNew, OperatorDelete };
+enum class CallableType : uint8_t {
+  // Unknown: probably function pointer.
+  Unknown,
+  Function,
+  Virtual,
+  Block
+};
+
+// Return whether a function's effects CAN be verified.
+// The question of whether it SHOULD be verified is independent.
+static bool functionIsVerifiable(const FunctionDecl *FD) {
+  if (FD->isTrivial()) {
+// Otherwise `struct x { int a; };` would have an unverifiable default
+// constructor.
+return true;
+  }
+  return FD->hasBody();
+}
+
+static bool isNoexcept(const FunctionDecl *FD) {
+  const auto *FPT = FD->getType()->castAs();

dougsonos wrote:

Thanks, this exposed that isNoexcept() was the one place that wasn't prepared 
for a prototype-less function type. For long-term safety I fixed that, but I 
also short-circuited the place that was calling it. In plain C, a terminating 
call like `abort()` can be detected and excluded from analysis with just 
`noreturn`, since `noexcept` isn't possible, and just too bad if it calls 
`longjmp()` (?).

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


[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

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


@@ -4940,6 +4955,78 @@ class FunctionEffectsRef {
   void dump(llvm::raw_ostream &OS) const;
 };
 
+/// A mutable set of FunctionEffect::Kind.
+class FunctionEffectKindSet {
+  // For now this only needs to be a bitmap.
+  constexpr static size_t EndBitPos = 8;
+  using KindBitsT = std::bitset;
+
+  KindBitsT KindBits{};
+
+  explicit FunctionEffectKindSet(KindBitsT KB) : KindBits(KB) {}
+
+  constexpr static size_t kindToPos(FunctionEffect::Kind K) {
+return static_cast(K);
+  }
+
+public:
+  FunctionEffectKindSet() = default;
+  explicit FunctionEffectKindSet(FunctionEffectsRef FX) { insert(FX); }
+
+  // Iterates through the bits which are set.
+  class iterator {

dougsonos wrote:

TIL. Thanks.

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


[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

2024-09-23 Thread Doug Wyatt via cfe-commits


@@ -0,0 +1,1572 @@
+//=== SemaFunctionEffects.cpp - Sema handling of function effects 
-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+//
+// This file implements Sema handling of function effects.
+//
+//===--===//
+
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/Stmt.h"
+#include "clang/AST/Type.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Sema/SemaInternal.h"
+
+#define DEBUG_TYPE "effectanalysis"
+
+using namespace clang;
+
+namespace {
+
+enum class ViolationID : uint8_t {
+  None = 0, // Sentinel for an empty Violation.
+  // These first few map to a %select{} in a diagnostic.
+  BaseDiagnosticIndex,
+  AllocatesMemory = BaseDiagnosticIndex,
+  ThrowsOrCatchesExceptions,
+  HasStaticLocalVariable,
+  AccessesThreadLocalVariable,
+  AccessesObjCMethodOrProperty,
+
+  // These only apply to callees, where the analysis stops at the Decl.
+  DeclDisallowsInference,
+
+  // These both apply to indirect calls. The difference is that sometimes
+  // we have an actual Decl (generally a variable) which is the function
+  // pointer being called, and sometimes, typically due to a cast, we only
+  // have an expression.
+  CallsDeclWithoutEffect,
+  CallsExprWithoutEffect,
+};
+
+// Information about the AST context in which a violation was found, so
+// that diagnostics can point to the correct source.
+class ViolationSite {
+public:
+  enum class Kind : uint8_t {
+Default = 0, // Function body.
+MemberInitializer = 1,
+DefaultArgExpr = 2
+  };
+
+private:
+  llvm::PointerIntPair Impl;
+
+public:
+  ViolationSite() = default;
+
+  explicit ViolationSite(CXXDefaultArgExpr *E)
+  : Impl(E, Kind::DefaultArgExpr) {}
+
+  Kind kind() const { return static_cast(Impl.getInt()); }
+  CXXDefaultArgExpr *defaultArgExpr() const { return Impl.getPointer(); }
+
+  void setKind(Kind K) { Impl.setPointerAndInt(nullptr, K); }
+};
+
+// Represents a violation of the rules, potentially for the entire duration of
+// the analysis phase, in order to refer to it when explaining why a caller has
+// been made unsafe by a callee. Can be transformed into either a Diagnostic
+// (warning or a note), depending on whether the violation pertains to a
+// function failing to be verifed as holding an effect vs. a function failing 
to
+// be inferred as holding that effect.
+struct Violation {
+  FunctionEffect Effect;
+  FunctionEffect
+  CalleeEffectPreventingInference; // Only for certain IDs; can be None.
+  ViolationID ID = ViolationID::None;
+  ViolationSite Site;
+  SourceLocation Loc;
+  const Decl *Callee = nullptr; // Only valid for Calls*.
+
+  Violation() = default;
+
+  Violation(FunctionEffect Effect, ViolationID ID, ViolationSite VS,
+SourceLocation Loc, const Decl *Callee = nullptr,
+std::optional CalleeEffect = std::nullopt)
+  : Effect(Effect), CalleeEffectPreventingInference(
+CalleeEffect.value_or(FunctionEffect())),
+ID(ID), Site(VS), Loc(Loc), Callee(Callee) {}
+
+  unsigned diagnosticSelectIndex() const {
+return unsigned(ID) - unsigned(ViolationID::BaseDiagnosticIndex);
+  }
+};
+
+enum class SpecialFuncType : uint8_t { None, OperatorNew, OperatorDelete };
+enum class CallableType : uint8_t {
+  // Unknown: probably function pointer.
+  Unknown,
+  Function,
+  Virtual,
+  Block
+};
+
+// Return whether a function's effects CAN be verified.
+// The question of whether it SHOULD be verified is independent.
+static bool functionIsVerifiable(const FunctionDecl *FD) {
+  if (FD->isTrivial()) {
+// Otherwise `struct x { int a; };` would have an unverifiable default
+// constructor.
+return true;
+  }
+  return FD->hasBody();
+}
+
+static bool isNoexcept(const FunctionDecl *FD) {
+  const auto *FPT = FD->getType()->castAs();
+  if (FPT->isNothrow() || FD->hasAttr())
+return true;
+  return false;
+}
+
+// This list is probably incomplete.
+// FIXME: Investigate:
+// __builtin_eh_return?
+// __builtin_allow_runtime_check?
+// __builtin_unwind_init and other similar things that sound exception-related.
+// va_copy?
+// coroutines?
+static FunctionEffectKindSet getBuiltinFunctionEffects(unsigned BuiltinID) {
+  FunctionEffectKindSet Result;
+
+  switch (BuiltinID) {
+  case 0:  // Not builtin.
+  default: // By default, builtins have no known effects.
+break;
+
+  // These allocate/deallocate heap memory.
+  case Builtin::ID::BI__builtin_calloc:
+  case Builtin::ID::BI__builtin_malloc:
+  case Builtin::ID::BI__builtin_realloc:
+  case Builtin::ID::BI__builtin_free:
+  case Builtin::ID:

[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

2024-09-23 Thread Doug Wyatt via cfe-commits


@@ -0,0 +1,1572 @@
+//=== SemaFunctionEffects.cpp - Sema handling of function effects 
-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+//
+// This file implements Sema handling of function effects.
+//
+//===--===//
+
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/Stmt.h"
+#include "clang/AST/Type.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Sema/SemaInternal.h"
+
+#define DEBUG_TYPE "effectanalysis"
+
+using namespace clang;
+
+namespace {
+
+enum class ViolationID : uint8_t {
+  None = 0, // Sentinel for an empty Violation.
+  // These first few map to a %select{} in a diagnostic.
+  BaseDiagnosticIndex,
+  AllocatesMemory = BaseDiagnosticIndex,
+  ThrowsOrCatchesExceptions,
+  HasStaticLocalVariable,
+  AccessesThreadLocalVariable,
+  AccessesObjCMethodOrProperty,
+
+  // These only apply to callees, where the analysis stops at the Decl.
+  DeclDisallowsInference,
+
+  // These both apply to indirect calls. The difference is that sometimes
+  // we have an actual Decl (generally a variable) which is the function
+  // pointer being called, and sometimes, typically due to a cast, we only
+  // have an expression.
+  CallsDeclWithoutEffect,
+  CallsExprWithoutEffect,
+};
+
+// Information about the AST context in which a violation was found, so
+// that diagnostics can point to the correct source.
+class ViolationSite {
+public:
+  enum class Kind : uint8_t {
+Default = 0, // Function body.
+MemberInitializer = 1,
+DefaultArgExpr = 2
+  };
+
+private:
+  llvm::PointerIntPair Impl;
+
+public:
+  ViolationSite() = default;
+
+  explicit ViolationSite(CXXDefaultArgExpr *E)
+  : Impl(E, Kind::DefaultArgExpr) {}
+
+  Kind kind() const { return static_cast(Impl.getInt()); }
+  CXXDefaultArgExpr *defaultArgExpr() const { return Impl.getPointer(); }
+
+  void setKind(Kind K) { Impl.setPointerAndInt(nullptr, K); }
+};
+
+// Represents a violation of the rules, potentially for the entire duration of
+// the analysis phase, in order to refer to it when explaining why a caller has
+// been made unsafe by a callee. Can be transformed into either a Diagnostic
+// (warning or a note), depending on whether the violation pertains to a
+// function failing to be verifed as holding an effect vs. a function failing 
to
+// be inferred as holding that effect.
+struct Violation {
+  FunctionEffect Effect;
+  FunctionEffect
+  CalleeEffectPreventingInference; // Only for certain IDs; can be None.
+  ViolationID ID = ViolationID::None;
+  ViolationSite Site;
+  SourceLocation Loc;
+  const Decl *Callee = nullptr; // Only valid for Calls*.
+
+  Violation() = default;
+
+  Violation(FunctionEffect Effect, ViolationID ID, ViolationSite VS,
+SourceLocation Loc, const Decl *Callee = nullptr,
+std::optional CalleeEffect = std::nullopt)
+  : Effect(Effect), CalleeEffectPreventingInference(
+CalleeEffect.value_or(FunctionEffect())),
+ID(ID), Site(VS), Loc(Loc), Callee(Callee) {}
+
+  unsigned diagnosticSelectIndex() const {
+return unsigned(ID) - unsigned(ViolationID::BaseDiagnosticIndex);
+  }
+};
+
+enum class SpecialFuncType : uint8_t { None, OperatorNew, OperatorDelete };
+enum class CallableType : uint8_t {
+  // Unknown: probably function pointer.
+  Unknown,
+  Function,
+  Virtual,
+  Block
+};
+
+// Return whether a function's effects CAN be verified.
+// The question of whether it SHOULD be verified is independent.
+static bool functionIsVerifiable(const FunctionDecl *FD) {
+  if (FD->isTrivial()) {
+// Otherwise `struct x { int a; };` would have an unverifiable default
+// constructor.
+return true;
+  }
+  return FD->hasBody();
+}
+
+static bool isNoexcept(const FunctionDecl *FD) {
+  const auto *FPT = FD->getType()->castAs();

dougsonos wrote:

Yeah, in attr-nonblocking-sema.c:

```
// Tests for a few cases involving C functions without prototypes.

void noproto() __attribute__((nonblocking)) // expected-error {{'nonblocking' 
function must have a prototype}}
{
}

// This will succeed
void noproto(void) __attribute__((blocking));

// A redeclaration isn't any different - a prototype is required.
void f1(void);
void f1() __attribute__((nonblocking)); // expected-error {{'nonblocking' 
function must have a prototype}}
```

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


[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

2024-09-23 Thread Doug Wyatt via cfe-commits


@@ -4940,6 +4955,78 @@ class FunctionEffectsRef {
   void dump(llvm::raw_ostream &OS) const;
 };
 
+/// A mutable set of FunctionEffect::Kind.
+class FunctionEffectKindSet {
+  // For now this only needs to be a bitmap.
+  constexpr static size_t EndBitPos = 8;
+  using KindBitsT = std::bitset;
+
+  KindBitsT KindBits{};
+
+  explicit FunctionEffectKindSet(KindBitsT KB) : KindBits(KB) {}
+
+  constexpr static size_t kindToPos(FunctionEffect::Kind K) {
+return static_cast(K);
+  }
+
+public:
+  FunctionEffectKindSet() = default;
+  explicit FunctionEffectKindSet(FunctionEffectsRef FX) { insert(FX); }
+
+  // Iterates through the bits which are set.
+  class iterator {

dougsonos wrote:

Wow, it still builds when private, but I don't understand. Apparently the `for` 
loops which walk through this thing aren't being foiled by the privateness of 
the iterator.

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


[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

2024-09-23 Thread Doug Wyatt via cfe-commits

dougsonos wrote:

@Sirraide , glad you're back and better, and thanks for the comments.

I think I agree and will remove the new diagnostics from "-Wall".

I also agree that documentation is wanting. I will look into expanding this, 
whether in the general attributes documentation or a new page (I'm speaking 
from a rusty memory of precedents, will go explore).

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


[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

2024-09-08 Thread Doug Wyatt via cfe-commits

dougsonos wrote:

> I do agree, it seems reasonable for it to be in `-Wall` or similar! That is 
> absolutely what I'd expect as a user :)

I guess my thinking was colored a bit by the way many of our codebases use 
`-Wall`.

I did come around to thinking that without `-Wall`, it's cleaner for the 
diagnostics to be disabled by default. Pushed a change.

Thanks, Chris.

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


[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

2024-09-08 Thread Doug Wyatt via cfe-commits

dougsonos wrote:

In working with this version of the compiler, I've discovered a pain point with 
libc++, `std::__libcpp_verbose_abort()`. Many things which one would expect to 
be nonblocking, e.g. `std::vector::operator[]`, have hardening paths which 
call `__libcpp_verbose_abort()` on failure.

I chatted with a libc++ maintainer about this. A first thought was to simply 
declare `__libcpp_verbose_abort()` as `nonblocking`. But that feels like a 
weird lie. Possibly most ideally, functions like this would have an attribute 
to exempt them from nonblocking analysis.

A quick hack would be to synthesize that attribute from a combination of the 
`noreturn` attribute and the function name containing "abort" or "terminate". 
(`noreturn` on its own is initially attractive, but it can also apply to a 
wrapper around `throw`).

The only workaround is to redeclare `__libcpp_verbose_abort()` with 
`[[clang::nonblocking]]`. This is tricky because the redeclaration has to 
follow the one in `<__verbose_abort>` but precede its use from other headers 
like ``. That leads to having to include `<__verbose_abort>` directly.

Would appreciate any thoughts!

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


[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

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

dougsonos wrote:

> Hi @dougsonos
> 
> We’re experiencing an unforeseen pain point trying to use rtsan without 
> function effects, and wanted to ask **how you would feel about making 
> function effect warnings opt-in rather than opt-out.**
> 
> While users can easily opt in to function effects and not rtsan, the problem 
> is that they can’t easily opt in to rtsan and not function effects.
> 
> Here’s why: someone wanting to try out rtsan can add the `[[nonblocking]]` 
> attribute, but this automatically opts them in to function effect warnings. 
> For users who compile with `-Werror`, this means they will likely be unable 
> to compile the code they wish to test with rtsan unless they explicitly turn 
> off function effects warnings with -Wno-function-effects. If they’re not 
> familiar with function effects they won’t know this, and we’re worried about 
> an education gap causing them to blame rtsan and give up on it before 
> realizing they can flick the function effects warnings off.
> 
> By disabling these warnings by default, both tools have the same "activation" 
> of attribute + compile time flag, and it is equally easy to run either tool 
> in isolation, or together.

My current understanding is that Clang has groups of warnings like `-Wmost` 
`-Wall` and `-Wextra` though I haven't seen yet how those work under the hood. 
My sense is that it would be weird for `-Wall` not to include 
`-Wfunction-effects`, but that it would be OK for `-Wfunction-effects` not to 
be enabled by default.

I do wonder, though, would it be that difficult to tell users to include 
`-Wno-function-effects` with `-fsanitize=realtime` (or whatever it is)?

Hopefully one of the experienced maintainers will chime in here.

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


[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

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


@@ -0,0 +1,1566 @@
+//=== SemaFunctionEffects.cpp - Sema handling of function effects 
-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+//
+// This file implements Sema handling of function effects.
+//
+//===--===//
+
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/Stmt.h"
+#include "clang/AST/Type.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Sema/SemaInternal.h"
+
+#define DEBUG_TYPE "effectanalysis"
+
+using namespace clang;
+
+namespace {
+
+enum class ViolationID : uint8_t {
+  None = 0, // Sentinel for an empty Violation.
+  // These first few map to a %select{} in a diagnostic.
+  BaseDiagnosticIndex,
+  AllocatesMemory = BaseDiagnosticIndex,
+  ThrowsOrCatchesExceptions,
+  HasStaticLocalVariable,
+  AccessesThreadLocalVariable,
+  AccessesObjCMethodOrProperty,
+
+  // These only apply to callees, where the analysis stops at the Decl.
+  DeclDisallowsInference,
+
+  // These both apply to indirect calls. The difference is that sometimes
+  // we have an actual Decl (generally a variable) which is the function
+  // pointer being called, and sometimes, typically due to a cast, we only
+  // have an expression.
+  CallsDeclWithoutEffect,
+  CallsExprWithoutEffect,
+};
+
+// Information about the AST context in which a violation was found, so
+// that diagnostics can point to the correct source.
+class ViolationSite {
+public:
+  enum class Kind : uint8_t {
+Default = 0, // Function body.
+MemberInitializer = 1,
+DefaultArgExpr = 2
+  };
+
+private:
+  llvm::PointerIntPair Impl;
+
+public:
+  ViolationSite() = default;
+
+  explicit ViolationSite(CXXDefaultArgExpr *E)
+  : Impl(E, Kind::DefaultArgExpr) {}
+
+  Kind kind() const { return static_cast(Impl.getInt()); }
+  CXXDefaultArgExpr *defaultArgExpr() const { return Impl.getPointer(); }
+
+  void setKind(Kind K) { Impl.setPointerAndInt(nullptr, K); }
+};
+
+// Represents a violation of the rules, potentially for the entire duration of
+// the analysis phase, in order to refer to it when explaining why a caller has
+// been made unsafe by a callee. Can be transformed into either a Diagnostic
+// (warning or a note), depending on whether the violation pertains to a
+// function failing to be verifed as holding an effect vs. a function failing 
to
+// be inferred as holding that effect.
+struct Violation {
+  FunctionEffect Effect;
+  FunctionEffect
+  CalleeEffectPreventingInference; // Only for certain IDs; can be None.
+  ViolationID ID = ViolationID::None;
+  ViolationSite Site;
+  SourceLocation Loc;
+  const Decl *Callee = nullptr; // Only valid for Calls*.
+
+  Violation() = default;
+
+  Violation(FunctionEffect Effect, ViolationID ID, ViolationSite VS,
+SourceLocation Loc, const Decl *Callee = nullptr,
+std::optional CalleeEffect = std::nullopt)
+  : Effect(Effect), ID(ID), Site(VS), Loc(Loc), Callee(Callee) {
+if (CalleeEffect)
+  CalleeEffectPreventingInference = *CalleeEffect;
+  }
+
+  unsigned diagnosticSelectIndex() const {
+return unsigned(ID) - unsigned(ViolationID::BaseDiagnosticIndex);
+  }
+};
+
+enum class SpecialFuncType : uint8_t { None, OperatorNew, OperatorDelete };
+enum class CallableType : uint8_t {
+  // Unknown: probably function pointer
+  Unknown,
+  Function,
+  Virtual,
+  Block
+};
+
+// Return whether a function's effects CAN be verified.
+// The question of whether it SHOULD be verified is independent.
+static bool functionIsVerifiable(const FunctionDecl *FD) {
+  if (FD->isTrivial()) {
+// Otherwise `struct x { int a; };` would have an unverifiable default
+// constructor.
+return true;
+  }
+  return FD->hasBody();
+}
+
+static bool isNoexcept(const FunctionDecl *FD) {
+  const auto *FPT = FD->getType()->castAs();
+  if (FPT->isNothrow() || FD->hasAttr())
+return true;
+  return false;
+}
+
+// This list is probably incomplete.
+// FIXME: Investigate:
+// __builtin_eh_return?
+// __builtin_allow_runtime_check?
+// __builtin_unwind_init and other similar things that sound exception-related.
+// va_copy?
+// coroutines?
+static FunctionEffectKindSet getBuiltinFunctionEffects(unsigned BuiltinID) {
+  FunctionEffectKindSet Result;
+
+  switch (BuiltinID) {
+  case 0:  // Not builtin.
+  default: // By default, builtins have no known effects.
+break;
+
+  // These allocate/deallocate heap memory.
+  case Builtin::ID::BI__builtin_calloc:
+  case Builtin::ID::BI__builtin_malloc:
+  case Builtin::ID::BI__builtin_realloc:
+  case Builtin::ID::BI__builtin_free:
+  case Builtin::ID::BI__builtin_operator_delete:
+

[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

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


@@ -8392,6 +8397,20 @@ void ASTReader::InitializeSema(Sema &S) {
 NewOverrides.applyOverrides(SemaObj->getLangOpts());
   }
 
+  if (!DeclsWithEffectsToVerify.empty()) {
+for (GlobalDeclID ID : DeclsWithEffectsToVerify) {
+  Decl *D = GetDecl(ID);
+  FunctionEffectsRef FX;
+  if (auto *FD = dyn_cast(D))
+FX = FD->getFunctionEffects();
+  else if (auto *BD = dyn_cast(D))
+FX = BD->getFunctionEffects();

dougsonos wrote:

Yeah, it's guarding against a pathological case where a function or block in 
`DeclsWithEffectsToVerify` turns out to have no effects. It happens that 
(despite comments) `addDeclWithEffects()` will still guard against this, so OK.

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


[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

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


@@ -0,0 +1,1566 @@
+//=== SemaFunctionEffects.cpp - Sema handling of function effects 
-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+//
+// This file implements Sema handling of function effects.
+//
+//===--===//
+
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/Stmt.h"
+#include "clang/AST/Type.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Sema/SemaInternal.h"
+
+#define DEBUG_TYPE "effectanalysis"
+
+using namespace clang;
+
+namespace {
+
+enum class ViolationID : uint8_t {
+  None = 0, // Sentinel for an empty Violation.
+  // These first few map to a %select{} in a diagnostic.
+  BaseDiagnosticIndex,
+  AllocatesMemory = BaseDiagnosticIndex,
+  ThrowsOrCatchesExceptions,
+  HasStaticLocalVariable,
+  AccessesThreadLocalVariable,
+  AccessesObjCMethodOrProperty,
+
+  // These only apply to callees, where the analysis stops at the Decl.
+  DeclDisallowsInference,
+
+  // These both apply to indirect calls. The difference is that sometimes
+  // we have an actual Decl (generally a variable) which is the function
+  // pointer being called, and sometimes, typically due to a cast, we only
+  // have an expression.
+  CallsDeclWithoutEffect,
+  CallsExprWithoutEffect,
+};
+
+// Information about the AST context in which a violation was found, so
+// that diagnostics can point to the correct source.
+class ViolationSite {
+public:
+  enum class Kind : uint8_t {
+Default = 0, // Function body.
+MemberInitializer = 1,
+DefaultArgExpr = 2
+  };
+
+private:
+  llvm::PointerIntPair Impl;
+
+public:
+  ViolationSite() = default;
+
+  explicit ViolationSite(CXXDefaultArgExpr *E)
+  : Impl(E, Kind::DefaultArgExpr) {}
+
+  Kind kind() const { return static_cast(Impl.getInt()); }
+  CXXDefaultArgExpr *defaultArgExpr() const { return Impl.getPointer(); }
+
+  void setKind(Kind K) { Impl.setPointerAndInt(nullptr, K); }
+};
+
+// Represents a violation of the rules, potentially for the entire duration of
+// the analysis phase, in order to refer to it when explaining why a caller has
+// been made unsafe by a callee. Can be transformed into either a Diagnostic
+// (warning or a note), depending on whether the violation pertains to a
+// function failing to be verifed as holding an effect vs. a function failing 
to
+// be inferred as holding that effect.
+struct Violation {
+  FunctionEffect Effect;
+  FunctionEffect
+  CalleeEffectPreventingInference; // Only for certain IDs; can be None.
+  ViolationID ID = ViolationID::None;
+  ViolationSite Site;
+  SourceLocation Loc;
+  const Decl *Callee = nullptr; // Only valid for Calls*.
+
+  Violation() = default;
+
+  Violation(FunctionEffect Effect, ViolationID ID, ViolationSite VS,
+SourceLocation Loc, const Decl *Callee = nullptr,
+std::optional CalleeEffect = std::nullopt)
+  : Effect(Effect), ID(ID), Site(VS), Loc(Loc), Callee(Callee) {
+if (CalleeEffect)
+  CalleeEffectPreventingInference = *CalleeEffect;
+  }
+
+  unsigned diagnosticSelectIndex() const {
+return unsigned(ID) - unsigned(ViolationID::BaseDiagnosticIndex);
+  }
+};
+
+enum class SpecialFuncType : uint8_t { None, OperatorNew, OperatorDelete };
+enum class CallableType : uint8_t {
+  // Unknown: probably function pointer
+  Unknown,
+  Function,
+  Virtual,
+  Block
+};
+
+// Return whether a function's effects CAN be verified.
+// The question of whether it SHOULD be verified is independent.
+static bool functionIsVerifiable(const FunctionDecl *FD) {
+  if (FD->isTrivial()) {
+// Otherwise `struct x { int a; };` would have an unverifiable default
+// constructor.
+return true;
+  }
+  return FD->hasBody();
+}
+
+static bool isNoexcept(const FunctionDecl *FD) {
+  const auto *FPT = FD->getType()->castAs();
+  if (FPT->isNothrow() || FD->hasAttr())
+return true;
+  return false;
+}
+
+// This list is probably incomplete.
+// FIXME: Investigate:
+// __builtin_eh_return?
+// __builtin_allow_runtime_check?
+// __builtin_unwind_init and other similar things that sound exception-related.
+// va_copy?
+// coroutines?
+static FunctionEffectKindSet getBuiltinFunctionEffects(unsigned BuiltinID) {
+  FunctionEffectKindSet Result;
+
+  switch (BuiltinID) {
+  case 0:  // Not builtin.
+  default: // By default, builtins have no known effects.
+break;
+
+  // These allocate/deallocate heap memory.
+  case Builtin::ID::BI__builtin_calloc:
+  case Builtin::ID::BI__builtin_malloc:
+  case Builtin::ID::BI__builtin_realloc:
+  case Builtin::ID::BI__builtin_free:
+  case Builtin::ID::BI__builtin_operator_delete:
+

[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

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


@@ -0,0 +1,1566 @@
+//=== SemaFunctionEffects.cpp - Sema handling of function effects 
-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+//
+// This file implements Sema handling of function effects.
+//
+//===--===//
+
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/Stmt.h"
+#include "clang/AST/Type.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Sema/SemaInternal.h"
+
+#define DEBUG_TYPE "effectanalysis"
+
+using namespace clang;
+
+namespace {
+
+enum class ViolationID : uint8_t {
+  None = 0, // Sentinel for an empty Violation.
+  // These first few map to a %select{} in a diagnostic.
+  BaseDiagnosticIndex,
+  AllocatesMemory = BaseDiagnosticIndex,
+  ThrowsOrCatchesExceptions,
+  HasStaticLocalVariable,
+  AccessesThreadLocalVariable,
+  AccessesObjCMethodOrProperty,
+
+  // These only apply to callees, where the analysis stops at the Decl.
+  DeclDisallowsInference,
+
+  // These both apply to indirect calls. The difference is that sometimes
+  // we have an actual Decl (generally a variable) which is the function
+  // pointer being called, and sometimes, typically due to a cast, we only
+  // have an expression.
+  CallsDeclWithoutEffect,
+  CallsExprWithoutEffect,
+};
+
+// Information about the AST context in which a violation was found, so
+// that diagnostics can point to the correct source.
+class ViolationSite {
+public:
+  enum class Kind : uint8_t {
+Default = 0, // Function body.
+MemberInitializer = 1,
+DefaultArgExpr = 2
+  };
+
+private:
+  llvm::PointerIntPair Impl;
+
+public:
+  ViolationSite() = default;
+
+  explicit ViolationSite(CXXDefaultArgExpr *E)
+  : Impl(E, Kind::DefaultArgExpr) {}
+
+  Kind kind() const { return static_cast(Impl.getInt()); }
+  CXXDefaultArgExpr *defaultArgExpr() const { return Impl.getPointer(); }
+
+  void setKind(Kind K) { Impl.setPointerAndInt(nullptr, K); }
+};
+
+// Represents a violation of the rules, potentially for the entire duration of
+// the analysis phase, in order to refer to it when explaining why a caller has
+// been made unsafe by a callee. Can be transformed into either a Diagnostic
+// (warning or a note), depending on whether the violation pertains to a
+// function failing to be verifed as holding an effect vs. a function failing 
to
+// be inferred as holding that effect.
+struct Violation {
+  FunctionEffect Effect;
+  FunctionEffect
+  CalleeEffectPreventingInference; // Only for certain IDs; can be None.
+  ViolationID ID = ViolationID::None;
+  ViolationSite Site;
+  SourceLocation Loc;
+  const Decl *Callee = nullptr; // Only valid for Calls*.
+
+  Violation() = default;
+
+  Violation(FunctionEffect Effect, ViolationID ID, ViolationSite VS,
+SourceLocation Loc, const Decl *Callee = nullptr,
+std::optional CalleeEffect = std::nullopt)
+  : Effect(Effect), ID(ID), Site(VS), Loc(Loc), Callee(Callee) {
+if (CalleeEffect)
+  CalleeEffectPreventingInference = *CalleeEffect;
+  }
+
+  unsigned diagnosticSelectIndex() const {
+return unsigned(ID) - unsigned(ViolationID::BaseDiagnosticIndex);
+  }
+};
+
+enum class SpecialFuncType : uint8_t { None, OperatorNew, OperatorDelete };
+enum class CallableType : uint8_t {
+  // Unknown: probably function pointer
+  Unknown,
+  Function,
+  Virtual,
+  Block
+};
+
+// Return whether a function's effects CAN be verified.
+// The question of whether it SHOULD be verified is independent.
+static bool functionIsVerifiable(const FunctionDecl *FD) {
+  if (FD->isTrivial()) {
+// Otherwise `struct x { int a; };` would have an unverifiable default
+// constructor.
+return true;
+  }
+  return FD->hasBody();
+}
+
+static bool isNoexcept(const FunctionDecl *FD) {
+  const auto *FPT = FD->getType()->castAs();
+  if (FPT->isNothrow() || FD->hasAttr())
+return true;
+  return false;
+}
+
+// This list is probably incomplete.
+// FIXME: Investigate:
+// __builtin_eh_return?
+// __builtin_allow_runtime_check?
+// __builtin_unwind_init and other similar things that sound exception-related.
+// va_copy?
+// coroutines?
+static FunctionEffectKindSet getBuiltinFunctionEffects(unsigned BuiltinID) {
+  FunctionEffectKindSet Result;
+
+  switch (BuiltinID) {
+  case 0:  // Not builtin.
+  default: // By default, builtins have no known effects.
+break;
+
+  // These allocate/deallocate heap memory.
+  case Builtin::ID::BI__builtin_calloc:
+  case Builtin::ID::BI__builtin_malloc:
+  case Builtin::ID::BI__builtin_realloc:
+  case Builtin::ID::BI__builtin_free:
+  case Builtin::ID::BI__builtin_operator_delete:
+

[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

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


@@ -0,0 +1,1566 @@
+//=== SemaFunctionEffects.cpp - Sema handling of function effects 
-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+//
+// This file implements Sema handling of function effects.
+//
+//===--===//
+
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/Stmt.h"
+#include "clang/AST/Type.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Sema/SemaInternal.h"
+
+#define DEBUG_TYPE "effectanalysis"
+
+using namespace clang;
+
+namespace {
+
+enum class ViolationID : uint8_t {
+  None = 0, // Sentinel for an empty Violation.
+  // These first few map to a %select{} in a diagnostic.
+  BaseDiagnosticIndex,
+  AllocatesMemory = BaseDiagnosticIndex,
+  ThrowsOrCatchesExceptions,
+  HasStaticLocalVariable,
+  AccessesThreadLocalVariable,
+  AccessesObjCMethodOrProperty,
+
+  // These only apply to callees, where the analysis stops at the Decl.
+  DeclDisallowsInference,
+
+  // These both apply to indirect calls. The difference is that sometimes
+  // we have an actual Decl (generally a variable) which is the function
+  // pointer being called, and sometimes, typically due to a cast, we only
+  // have an expression.
+  CallsDeclWithoutEffect,
+  CallsExprWithoutEffect,
+};
+
+// Information about the AST context in which a violation was found, so
+// that diagnostics can point to the correct source.
+class ViolationSite {
+public:
+  enum class Kind : uint8_t {
+Default = 0, // Function body.
+MemberInitializer = 1,
+DefaultArgExpr = 2
+  };
+
+private:
+  llvm::PointerIntPair Impl;
+
+public:
+  ViolationSite() = default;
+
+  explicit ViolationSite(CXXDefaultArgExpr *E)
+  : Impl(E, Kind::DefaultArgExpr) {}
+
+  Kind kind() const { return static_cast(Impl.getInt()); }
+  CXXDefaultArgExpr *defaultArgExpr() const { return Impl.getPointer(); }
+
+  void setKind(Kind K) { Impl.setPointerAndInt(nullptr, K); }
+};
+
+// Represents a violation of the rules, potentially for the entire duration of
+// the analysis phase, in order to refer to it when explaining why a caller has
+// been made unsafe by a callee. Can be transformed into either a Diagnostic
+// (warning or a note), depending on whether the violation pertains to a
+// function failing to be verifed as holding an effect vs. a function failing 
to
+// be inferred as holding that effect.
+struct Violation {
+  FunctionEffect Effect;
+  FunctionEffect
+  CalleeEffectPreventingInference; // Only for certain IDs; can be None.
+  ViolationID ID = ViolationID::None;
+  ViolationSite Site;
+  SourceLocation Loc;
+  const Decl *Callee = nullptr; // Only valid for Calls*.
+
+  Violation() = default;
+
+  Violation(FunctionEffect Effect, ViolationID ID, ViolationSite VS,
+SourceLocation Loc, const Decl *Callee = nullptr,
+std::optional CalleeEffect = std::nullopt)
+  : Effect(Effect), ID(ID), Site(VS), Loc(Loc), Callee(Callee) {
+if (CalleeEffect)
+  CalleeEffectPreventingInference = *CalleeEffect;
+  }
+
+  unsigned diagnosticSelectIndex() const {
+return unsigned(ID) - unsigned(ViolationID::BaseDiagnosticIndex);
+  }
+};
+
+enum class SpecialFuncType : uint8_t { None, OperatorNew, OperatorDelete };
+enum class CallableType : uint8_t {
+  // Unknown: probably function pointer
+  Unknown,
+  Function,
+  Virtual,
+  Block
+};
+
+// Return whether a function's effects CAN be verified.
+// The question of whether it SHOULD be verified is independent.
+static bool functionIsVerifiable(const FunctionDecl *FD) {
+  if (FD->isTrivial()) {
+// Otherwise `struct x { int a; };` would have an unverifiable default
+// constructor.
+return true;
+  }
+  return FD->hasBody();
+}
+
+static bool isNoexcept(const FunctionDecl *FD) {
+  const auto *FPT = FD->getType()->castAs();
+  if (FPT->isNothrow() || FD->hasAttr())
+return true;
+  return false;
+}
+
+// This list is probably incomplete.
+// FIXME: Investigate:
+// __builtin_eh_return?
+// __builtin_allow_runtime_check?
+// __builtin_unwind_init and other similar things that sound exception-related.
+// va_copy?
+// coroutines?
+static FunctionEffectKindSet getBuiltinFunctionEffects(unsigned BuiltinID) {
+  FunctionEffectKindSet Result;
+
+  switch (BuiltinID) {
+  case 0:  // Not builtin.
+  default: // By default, builtins have no known effects.
+break;
+
+  // These allocate/deallocate heap memory.
+  case Builtin::ID::BI__builtin_calloc:
+  case Builtin::ID::BI__builtin_malloc:
+  case Builtin::ID::BI__builtin_realloc:
+  case Builtin::ID::BI__builtin_free:
+  case Builtin::ID::BI__builtin_operator_delete:
+

[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

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


@@ -0,0 +1,1566 @@
+//=== SemaFunctionEffects.cpp - Sema handling of function effects 
-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+//
+// This file implements Sema handling of function effects.
+//
+//===--===//
+
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/Stmt.h"
+#include "clang/AST/Type.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Sema/SemaInternal.h"
+
+#define DEBUG_TYPE "effectanalysis"

dougsonos wrote:

This works with LLVM_DEBUG to enable debug logging and only in debug builds.

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


[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

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

https://github.com/dougsonos edited 
https://github.com/llvm/llvm-project/pull/99656
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

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


@@ -10950,6 +10950,51 @@ def warn_imp_cast_drops_unaligned : Warning<
   InGroup>;
 
 // Function effects
+def warn_func_effect_violation : Warning<
+  "'%0' %select{function|constructor|destructor|lambda|block|constructor's 
member initializer}1 "

dougsonos wrote:

How about

lambda with 'nonblocking' attribute must not allocate or deallocate memory

?

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


[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

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


@@ -8392,6 +8397,20 @@ void ASTReader::InitializeSema(Sema &S) {
 NewOverrides.applyOverrides(SemaObj->getLangOpts());
   }
 
+  if (!DeclsWithEffectsToVerify.empty()) {
+for (GlobalDeclID ID : DeclsWithEffectsToVerify) {
+  Decl *D = GetDecl(ID);
+  FunctionEffectsRef FX;
+  if (auto *FD = dyn_cast(D))
+FX = FD->getFunctionEffects();
+  else if (auto *BD = dyn_cast(D))
+FX = BD->getFunctionEffects();

dougsonos wrote:

If it were just one call, I wouldn't care so much but that would be a 
repetition of two lines and I'm a bit OCD about DRY. If the concern is about 
the test of `FX.empty()` being applied in the case where it's never been 
assigned, a `continue` could fix that.

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


[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

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


@@ -5137,47 +5137,41 @@ StringRef FunctionEffect::name() const {
   llvm_unreachable("unknown effect kind");
 }
 
-bool FunctionEffect::canInferOnFunction(const Decl &Callee) const {
+std::optional FunctionEffect::effectProhibitingInference(
+const Decl &Callee, const FunctionEffectKindSet &CalleeFX) 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(&Callee))
-  CalleeFX = BD->getFunctionEffects();
-else
-  return false;
-for (const FunctionEffectWithCondition &CalleeEC : CalleeFX) {
+for (const FunctionEffect &Effect : CalleeFX) {
   // nonblocking/nonallocating cannot call allocating.
-  if (CalleeEC.Effect.kind() == Kind::Allocating)
-return false;
+  if (Effect.kind() == Kind::Allocating)
+return Effect;
   // nonblocking cannot call blocking.
-  if (kind() == Kind::NonBlocking &&
-  CalleeEC.Effect.kind() == Kind::Blocking)
-return false;
+  if (kind() == Kind::NonBlocking && Effect.kind() == Kind::Blocking)
+return Effect;
 }
-return true;
+return std::nullopt;
   }
 
   case Kind::Allocating:
   case Kind::Blocking:
-return false;
+assert(0 && "effectProhibitingInference with non-inferable effect kind");

dougsonos wrote:

Ah, I removed a more obviously superfluous assert for Kind::None, letting it 
fall into the llvm_unreachable. I didn't see this one at the time, and it does 
have a more useful message. Hm.

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


[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

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


@@ -15099,6 +15037,106 @@ class Sema final : public SemaBase {
   std::string getFixItZeroLiteralForType(QualType T, SourceLocation Loc) const;
 
   ///@}
+
+  //

dougsonos wrote:

It's copied from the other groupings which separate methods (etc) which are 
isolated to a single source file.

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


[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

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


@@ -10950,6 +10950,51 @@ def warn_imp_cast_drops_unaligned : Warning<
   InGroup>;
 
 // Function effects
+def warn_func_effect_violation : Warning<
+  "'%0' %select{function|constructor|destructor|lambda|block|constructor's 
member initializer}1 "

dougsonos wrote:

The `%0` at the beginning is the name of the constraining attribute, so the 
diagnostic looks something like:

'nonblocking' lambda must not allocate or deallocate memory

Is that sufficient?

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


[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

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


@@ -4932,6 +4938,78 @@ class FunctionEffectsRef {
   void dump(llvm::raw_ostream &OS) const;
 };
 
+/// A mutable set of FunctionEffect::Kind.
+class FunctionEffectKindSet {
+  // For now this only needs to be a bitmap.
+  constexpr static size_t EndBitPos = 8;
+  using KindBitsT = std::bitset;
+
+  KindBitsT KindBits{};
+
+  explicit FunctionEffectKindSet(KindBitsT KB) : KindBits(KB) {}
+
+  constexpr static size_t kindToPos(FunctionEffect::Kind K) {
+return size_t(K);

dougsonos wrote:

Oops, committed that before realizing it doesn't build -- and I'm not quite 
sure what you intended.

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


[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

2024-08-29 Thread Doug Wyatt via cfe-commits


@@ -1,44 +1,67 @@
-//=== EffectAnalysis.cpp - Sema warnings for function effects 
-===//
+//=== SemaFunctionEffects.cpp - Sema handling of function effects 
-===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 
//===--===//
 //
-// This file implements caller/callee analysis for function effects.
+// This file implements Sema handling of function effects.
 //
 
//===--===//
 
 #include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
 #include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/AST/Stmt.h"
 #include "clang/AST/Type.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/Sema/SemaInternal.h"
 
-#define DEBUG_TYPE "fxanalysis"
+#define DEBUG_TYPE "effectanalysis"
 
 using namespace clang;
 
 namespace {
 
 enum class ViolationID : uint8_t {
-  None = 0, // sentinel for an empty Violation
-  Throws,
-  Catches,
-  CallsObjC,
-  AllocatesMemory,
-  HasStaticLocal,
-  AccessesThreadLocal,
-
-  // These only apply to callees, where the analysis stops at the Decl
+  None = 0, // Sentinel for an empty Violation.
+  // These first few map to a %select{} in a diagnostic.
+  BaseDiagnosticIndex,
+  AllocatesMemory = BaseDiagnosticIndex,
+  ThrowsOrCatchesExceptions,
+  HasStaticLocalVariable,
+  AccessesThreadLocalVariable,
+  AccessesObjCMethodOrProperty,
+
+  // These only apply to callees, where the analysis stops at the Decl.
   DeclDisallowsInference,
 
+  // These both apply to indirect calls. The difference is that sometimes
+  // we have an actual Decl (generally a variable) which is the function
+  // pointer being called, and sometimes, typically due to a cast, we only
+  // have an expression.
   CallsDeclWithoutEffect,
   CallsExprWithoutEffect,
 };
 
+// Information about the AST context in which a violation was found, so
+// that diagnostics can point to the correct source.
+struct ViolationSite {
+  enum class Kind : uint8_t {
+Default = 0, // Function body.
+MemberInitializer = 1,
+DefaultArgExpr = 2
+  };
+
+  Kind VKind = Kind::Default;
+  CXXDefaultArgExpr *DefaultArgExpr = nullptr;

dougsonos wrote:

Good idea, thanks, done.

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


[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

2024-08-29 Thread Doug Wyatt via cfe-commits


@@ -908,9 +936,9 @@ class Analyzer {
 static bool isSafeBuiltinFunction(const FunctionDecl *FD) {
   unsigned BuiltinID = FD->getBuiltinID();
   switch (BuiltinID) {
-  case 0: // not builtin
+  case 0: // Not builtin.
 return false;
-  default: // not disallowed via cases below
+  default: // Not disallowed via cases below.
 return true;
 
   // Disallow list

dougsonos wrote:

I made a pass through Builtins.td and added a number of other functions. There 
could still be more.

I also refactored so that the helper function synthesizes a set of effects for 
a builtin function, so if/when TableGen supports this, it's clearer what to do.

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


[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

2024-08-29 Thread Doug Wyatt via cfe-commits


@@ -210,24 +210,88 @@ void nb18(void (^block)() [[clang::nonblocking]]) 
[[clang::nonblocking]]
 }
 
 // Builtin functions
-void nb18a() [[clang::nonblocking]] {
+void nb19() [[clang::nonblocking]] {
__builtin_assume(1);
void *ptr = __builtin_malloc(1); // expected-warning {{'nonblocking' 
function must not call non-'nonblocking' function '__builtin_malloc'}}
__builtin_free(ptr); // expected-warning {{'nonblocking' function must 
not call non-'nonblocking' function '__builtin_free'}}
+   
+   void *p2 = __builtin_operator_new(1); // expected-warning 
{{'nonblocking' function must not call non-'nonblocking' function 
'__builtin_operator_new'}}
+   __builtin_operator_delete(p2); // expected-warning {{'nonblocking' 
function must not call non-'nonblocking' function '__builtin_operator_delete'}}
+}
+
+// Function try-block
+void catches() try {} catch (...) {} // expected-note {{function cannot be 
inferred 'nonblocking' because it throws or catches exceptions}}
+
+void nb20() [[clang::nonblocking]] {
+   catches(); // expected-warning {{'nonblocking' function must not call 
non-'nonblocking' function 'catches'}}
+}
+
+struct S {
+int x;
+S(int x) try : x(x) {} catch (...) {} // expected-note {{constructor 
cannot be inferred 'nonblocking' because it throws or catches exceptions}}
+S(double) : x((throw 3, 3)) {} // expected-note {{member initializer 
cannot be inferred 'nonblocking' because it throws or catches exceptions}} \
+  expected-note {{in constructor here}}
+};
+
+int badi(); // expected-note {{declaration cannot be inferred 'nonblocking' 
because it has no definition in this translation unit}} \
+// expected-note {{declaration cannot be inferred 'nonblocking' 
because it has no definition in this translation unit}}
+
+struct A {// expected-note {{in implicit constructor here}}
+int x = (throw 3, 3); // expected-note {{member initializer cannot be 
inferred 'nonblocking' because it throws or catches exceptions}}
+};
+
+struct B {
+int y = badi(); // expected-note {{member initializer cannot be inferred 
'nonblocking' because it calls non-'nonblocking' function 'badi'}}
+};
+
+void f() [[clang::nonblocking]] {
+S s1(3);   // expected-warning {{'nonblocking' function must not call 
non-'nonblocking' constructor 'S::S'}}
+S s2(3.0); // expected-warning {{'nonblocking' function must not call 
non-'nonblocking' constructor 'S::S'}}
+A a;   // expected-warning {{'nonblocking' function must not call 
non-'nonblocking' constructor 'A::A'}}
+B b;   // expected-warning {{'nonblocking' function must not call 
non-'nonblocking' constructor 'B::B'}}
+}
+
+struct T {
+   int x = badi();   // expected-warning {{'nonblocking' 
constructor's member initializer must not call non-'nonblocking' function 
'badi'}}
+   T() [[clang::nonblocking]] {} // expected-note {{in constructor here}}
+   T(int x) [[clang::nonblocking]] : x(x) {} // OK
+};
+

dougsonos wrote:

Added  one. This revealed a bug: when verifying a destructor, both bases and 
virtual bases were being traversed but this meant that a virtual base would get 
visited twice. Fixed that by ignoring the virtual bases.

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


[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

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

dougsonos wrote:

I believe I have addressed all feedback so far.

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


[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

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


@@ -0,0 +1,256 @@
+// RUN: %clang_cc1 -fsyntax-only -fblocks -fcxx-exceptions -std=c++20 -verify 
%s
+// These are in a separate file because errors (e.g. incompatible attributes) 
currently prevent
+// the FXAnalysis pass from running at all.
+
+// This diagnostic is re-enabled and exercised in isolation later in this file.
+#pragma clang diagnostic ignored "-Wperf-constraint-implies-noexcept"
+
+// --- CONSTRAINTS ---
+
+void nb1() [[clang::nonblocking]]
+{
+   int *pInt = new int; // expected-warning {{'nonblocking' function must 
not allocate or deallocate memory}}
+   delete pInt; // expected-warning {{'nonblocking' function must not 
allocate or deallocate memory}}
+}
+
+void nb2() [[clang::nonblocking]]
+{
+   static int global; // expected-warning {{'nonblocking' function must 
not have static locals}}
+}
+
+void nb3() [[clang::nonblocking]]
+{
+   try {
+   throw 42; // expected-warning {{'nonblocking' function must not 
throw or catch exceptions}}
+   }
+   catch (...) { // expected-warning {{'nonblocking' function must not 
throw or catch exceptions}}
+   }
+}
+

dougsonos wrote:

OK, in the interest of not over-complicating things, I have not tried to 
detangle the way that default arguments are effectively visited twice, once at 
the function definition, and again at every call site.

There is however logic so that a warning pointing to a default argument is 
followed by a note at the call site which invoked it.

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


[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

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


@@ -5137,47 +5137,41 @@ StringRef FunctionEffect::name() const {
   llvm_unreachable("unknown effect kind");
 }
 
-bool FunctionEffect::canInferOnFunction(const Decl &Callee) const {
+std::optional FunctionEffect::effectProhibitingInference(
+const Decl &Callee, const FunctionEffectKindSet &CalleeFX) 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(&Callee))
-  CalleeFX = BD->getFunctionEffects();
-else
-  return false;
-for (const FunctionEffectWithCondition &CalleeEC : CalleeFX) {
+for (const FunctionEffect &Effect : CalleeFX) {
   // nonblocking/nonallocating cannot call allocating.
-  if (CalleeEC.Effect.kind() == Kind::Allocating)
-return false;
+  if (Effect.kind() == Kind::Allocating)
+return Effect;
   // nonblocking cannot call blocking.
-  if (kind() == Kind::NonBlocking &&
-  CalleeEC.Effect.kind() == Kind::Blocking)
-return false;
+  if (kind() == Kind::NonBlocking && Effect.kind() == Kind::Blocking)
+return Effect;
 }
-return true;
+return std::nullopt;
   }
 
   case Kind::Allocating:
   case Kind::Blocking:
-return false;
+assert(0 && "effectProhibitingInference with non-inferable effect kind");

dougsonos wrote:

I've addressed this for now by removing the `assert()`s and adjusting the 
`llvm_unreachable()` message.

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


[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

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


@@ -0,0 +1,256 @@
+// RUN: %clang_cc1 -fsyntax-only -fblocks -fcxx-exceptions -std=c++20 -verify 
%s
+// These are in a separate file because errors (e.g. incompatible attributes) 
currently prevent
+// the FXAnalysis pass from running at all.
+
+// This diagnostic is re-enabled and exercised in isolation later in this file.
+#pragma clang diagnostic ignored "-Wperf-constraint-implies-noexcept"
+
+// --- CONSTRAINTS ---
+
+void nb1() [[clang::nonblocking]]
+{
+   int *pInt = new int; // expected-warning {{'nonblocking' function must 
not allocate or deallocate memory}}
+   delete pInt; // expected-warning {{'nonblocking' function must not 
allocate or deallocate memory}}
+}
+
+void nb2() [[clang::nonblocking]]
+{
+   static int global; // expected-warning {{'nonblocking' function must 
not have static locals}}
+}
+
+void nb3() [[clang::nonblocking]]
+{
+   try {
+   throw 42; // expected-warning {{'nonblocking' function must not 
throw or catch exceptions}}
+   }
+   catch (...) { // expected-warning {{'nonblocking' function must not 
throw or catch exceptions}}
+   }
+}
+

dougsonos wrote:

Implemented. It only gracefully handles one level of `CXXDefaultArgExpr` (no 
nesting) but that should be a >90% case.

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


[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

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

https://github.com/dougsonos edited 
https://github.com/llvm/llvm-project/pull/99656
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)

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


@@ -0,0 +1,256 @@
+// RUN: %clang_cc1 -fsyntax-only -fblocks -fcxx-exceptions -std=c++20 -verify 
%s
+// These are in a separate file because errors (e.g. incompatible attributes) 
currently prevent
+// the FXAnalysis pass from running at all.
+
+// This diagnostic is re-enabled and exercised in isolation later in this file.
+#pragma clang diagnostic ignored "-Wperf-constraint-implies-noexcept"
+
+// --- CONSTRAINTS ---
+
+void nb1() [[clang::nonblocking]]
+{
+   int *pInt = new int; // expected-warning {{'nonblocking' function must 
not allocate or deallocate memory}}
+   delete pInt; // expected-warning {{'nonblocking' function must not 
allocate or deallocate memory}}
+}
+
+void nb2() [[clang::nonblocking]]
+{
+   static int global; // expected-warning {{'nonblocking' function must 
not have static locals}}
+}
+
+void nb3() [[clang::nonblocking]]
+{
+   try {
+   throw 42; // expected-warning {{'nonblocking' function must not 
throw or catch exceptions}}
+   }
+   catch (...) { // expected-warning {{'nonblocking' function must not 
throw or catch exceptions}}
+   }
+}
+

dougsonos wrote:

Also the AST visitor visits the default argument as part of both functions, 
which I guess is understandable, but for the purposes of this analysis it 
should only be seen as being evaluated by the caller, right?

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


  1   2   3   4   5   >