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

Doug Wyatt via cfe-commits cfe-commits at lists.llvm.org
Mon Nov 4 07:55:59 PST 2024


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 <dwyatt at apple.com>
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<CXXDestructorDecl>(CurrentCaller.CDecl))
         followDestructor(dyn_cast<CXXRecordDecl>(Dtor->getParent()), Dtor);
 
+      if (auto *FD = dyn_cast<FunctionDecl>(CurrentCaller.CDecl))
+        TrailingRequiresClause = FD->getTrailingRequiresClause();
+
       // Do an AST traversal of the function/block body
       TraverseDecl(const_cast<Decl *>(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 <dwyatt at apple.com>
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 <valarray>, preceding the inclusion of <expected>) is sufficiently complex
+// to look like it contains function calls. There may be simpler examples.
+
+namespace ExpectedTest {
+
+template <class _Tp>
+inline constexpr bool is_copy_constructible_v = __is_constructible(_Tp, _Tp&);
+
+template <bool, class _Tp = void>
+struct enable_if {};
+template <class _Tp>
+struct enable_if<true, _Tp> {
+  typedef _Tp type;
+};
+
+template <bool _Bp, class _Tp = void>
+using enable_if_t = typename enable_if<_Bp, _Tp>::type;
+
+// Doesn't seem to matter whether the enable_if is true or false.
+template <class E1, class E2, enable_if_t<is_copy_constructible_v<E1>> = 0>
+inline bool operator&&(const E1& x, const E2& y);
+
+template <class _Tp, class _Err>
+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<int, int> 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 <dwyatt at apple.com>
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;
     }



More information about the cfe-commits mailing list