[clang] [Sema] Permit side-effect-free function calls in C++23 [[assume]] (PR #194243)

via cfe-commits cfe-commits at lists.llvm.org
Sun Apr 26 18:18:11 PDT 2026


https://github.com/SekaiArendelle updated https://github.com/llvm/llvm-project/pull/194243

>From d1a64a230e2aae7234100d87426c10f646c94b28 Mon Sep 17 00:00:00 2001
From: Arendelle <Arendelle_ at outlook.com>
Date: Sun, 26 Apr 2026 15:18:22 +0800
Subject: [PATCH 01/12] [Sema] Permit side-effect-free function calls in C++23
 [[assume]]

The current implementation of `[[assume(expr)]]` rejects any expression
containing a function call as having side effects, because
`Expr::HasSideEffects` conservatively treats every call as impure.

This is overly restrictive for real-world code where developers want to
write things like:
    [[assume(i < vec.size())]];

Introduce a targeted interprocedural analysis
`HasSideEffectsForAssume` that recursively inspects function bodies to
prove they are side-effect free.  We still conservatively bail out on:

* indirect calls (function pointers),
* virtual calls (dynamic dispatch),
* calls to functions annotated `const` or `pure` (already handled by
  the attribute check),
* and cycles in the call graph.

When a callee's body contains only pure operations (no assignments,
no I/O, no volatile accesses, no further impure calls), we now allow
it inside an assumption expression.
---
 clang/lib/Sema/SemaStmtAttr.cpp | 95 ++++++++++++++++++++++++++++++++-
 1 file changed, 94 insertions(+), 1 deletion(-)

diff --git a/clang/lib/Sema/SemaStmtAttr.cpp b/clang/lib/Sema/SemaStmtAttr.cpp
index 58d5332565d10..140382acac9c9 100644
--- a/clang/lib/Sema/SemaStmtAttr.cpp
+++ b/clang/lib/Sema/SemaStmtAttr.cpp
@@ -802,6 +802,99 @@ ExprResult Sema::ActOnCXXAssumeAttr(Stmt *St, const ParsedAttr &A,
   return Assumption;
 }
 
+/// Recursively analyze whether a function body has side effects.
+/// \p Visited tracks the current call chain to prevent infinite recursion.
+static bool FunctionBodyHasSideEffects(const FunctionDecl *FD,
+                                        const ASTContext &Ctx,
+                                        SmallPtrSetImpl<const FunctionDecl*> &Visited) {
+  if (FD->hasAttr<ConstAttr>() || FD->hasAttr<PureAttr>())
+    return false;
+
+  const Stmt *Body = FD->getBody();
+  if (!Body)
+    return true;
+
+  if (!Visited.insert(FD).second)
+    return true;
+
+  bool HasSE = false;
+  SmallVector<const Stmt*, 16> Worklist;
+  Worklist.push_back(Body);
+
+  while (!Worklist.empty()) {
+    const Stmt *S = Worklist.pop_back_val();
+    if (!S)
+      continue;
+
+    if (const Expr *E = dyn_cast<Expr>(S)) {
+      // Check for definite side effects other than function calls.
+      if (E->HasSideEffects(Ctx, /*IncludePossibleEffects=*/false)) {
+        HasSE = true;
+        break;
+      }
+
+      // Recursively check callee bodies for nested calls.
+      if (const CallExpr *CE = dyn_cast<CallExpr>(E)) {
+        if (const FunctionDecl *Callee = CE->getDirectCallee()) {
+          if (FunctionBodyHasSideEffects(Callee, Ctx, Visited)) {
+            HasSE = true;
+            break;
+          }
+        } else {
+          HasSE = true;
+          break;
+        }
+      }
+    }
+
+    for (const Stmt *Child : S->children())
+      Worklist.push_back(Child);
+  }
+
+  Visited.erase(FD);
+  return HasSE;
+}
+
+/// Check whether an expression in [[assume]] has side effects.
+/// This is more precise than Expr::HasSideEffects because it attempts to
+/// analyze function bodies to prove that simple functions are side-effect free.
+static bool HasSideEffectsForAssume(const Expr *E, const ASTContext &Ctx) {
+  // First, filter out definite side effects like assignments, increments,
+  // new/delete, volatile accesses, etc.  In this mode, function calls are
+  // ignored (their arguments are still checked).
+  if (E->HasSideEffects(Ctx, /*IncludePossibleEffects=*/false))
+    return true;
+
+  // Second, walk the expression and check every CallExpr's function body.
+  SmallVector<const Expr*, 16> Worklist;
+  Worklist.push_back(E);
+
+  while (!Worklist.empty()) {
+    const Expr *SubExpr = Worklist.pop_back_val();
+
+    if (const CallExpr *CE = dyn_cast<CallExpr>(SubExpr)) {
+      if (const FunctionDecl *FD = CE->getDirectCallee()) {
+        SmallPtrSet<const FunctionDecl*, 8> Visited;
+        if (FunctionBodyHasSideEffects(FD, Ctx, Visited))
+          return true;
+      } else {
+        return true;
+      }
+
+      // Check nested calls inside arguments (e.g. foo(bar())).
+      for (const Expr *Arg : CE->arguments())
+        Worklist.push_back(Arg);
+    } else {
+      for (const Stmt *Child : SubExpr->children()) {
+        if (const Expr *ChildExpr = dyn_cast_or_null<Expr>(Child))
+          Worklist.push_back(ChildExpr);
+      }
+    }
+  }
+
+  return false;
+}
+
 ExprResult Sema::BuildCXXAssumeExpr(Expr *Assumption,
                                     const IdentifierInfo *AttrName,
                                     SourceRange Range) {
@@ -821,7 +914,7 @@ ExprResult Sema::BuildCXXAssumeExpr(Expr *Assumption,
     return ExprError();
 
   Assumption = Res.get();
-  if (Assumption->HasSideEffects(Context))
+  if (HasSideEffectsForAssume(Assumption, Context))
     Diag(Assumption->getBeginLoc(), diag::warn_assume_side_effects)
         << AttrName << Range;
 

>From d3f8c762856bbb971594e4086ad1716962a2ee00 Mon Sep 17 00:00:00 2001
From: Arendelle <Arendelle_ at outlook.com>
Date: Sun, 26 Apr 2026 15:25:27 +0800
Subject: [PATCH 02/12] bugfix: handle AsmStmt

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

diff --git a/clang/lib/Sema/SemaStmtAttr.cpp b/clang/lib/Sema/SemaStmtAttr.cpp
index 140382acac9c9..13b00084d305c 100644
--- a/clang/lib/Sema/SemaStmtAttr.cpp
+++ b/clang/lib/Sema/SemaStmtAttr.cpp
@@ -826,6 +826,11 @@ static bool FunctionBodyHasSideEffects(const FunctionDecl *FD,
     if (!S)
       continue;
 
+    if (isa<AsmStmt>(S)) {
+      HasSE = true;
+      break;
+    }
+
     if (const Expr *E = dyn_cast<Expr>(S)) {
       // Check for definite side effects other than function calls.
       if (E->HasSideEffects(Ctx, /*IncludePossibleEffects=*/false)) {

>From 0d3b137c8aa1f6ac27d82d6e4673513c7558369e Mon Sep 17 00:00:00 2001
From: Arendelle <Arendelle_ at outlook.com>
Date: Sun, 26 Apr 2026 15:40:58 +0800
Subject: [PATCH 03/12] fix analysis of nested calls

- and some format issues
---
 clang/lib/Sema/SemaStmtAttr.cpp | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/clang/lib/Sema/SemaStmtAttr.cpp b/clang/lib/Sema/SemaStmtAttr.cpp
index 13b00084d305c..db2dd812114ad 100644
--- a/clang/lib/Sema/SemaStmtAttr.cpp
+++ b/clang/lib/Sema/SemaStmtAttr.cpp
@@ -806,7 +806,7 @@ ExprResult Sema::ActOnCXXAssumeAttr(Stmt *St, const ParsedAttr &A,
 /// \p Visited tracks the current call chain to prevent infinite recursion.
 static bool FunctionBodyHasSideEffects(const FunctionDecl *FD,
                                         const ASTContext &Ctx,
-                                        SmallPtrSetImpl<const FunctionDecl*> &Visited) {
+                                        SmallPtrSetImpl<const FunctionDecl *> &Visited) {
   if (FD->hasAttr<ConstAttr>() || FD->hasAttr<PureAttr>())
     return false;
 
@@ -818,7 +818,7 @@ static bool FunctionBodyHasSideEffects(const FunctionDecl *FD,
     return true;
 
   bool HasSE = false;
-  SmallVector<const Stmt*, 16> Worklist;
+  SmallVector<const Stmt *, 16> Worklist;
   Worklist.push_back(Body);
 
   while (!Worklist.empty()) {
@@ -871,7 +871,7 @@ static bool HasSideEffectsForAssume(const Expr *E, const ASTContext &Ctx) {
     return true;
 
   // Second, walk the expression and check every CallExpr's function body.
-  SmallVector<const Expr*, 16> Worklist;
+  SmallVector<const Expr *, 16> Worklist;
   Worklist.push_back(E);
 
   while (!Worklist.empty()) {
@@ -879,7 +879,7 @@ static bool HasSideEffectsForAssume(const Expr *E, const ASTContext &Ctx) {
 
     if (const CallExpr *CE = dyn_cast<CallExpr>(SubExpr)) {
       if (const FunctionDecl *FD = CE->getDirectCallee()) {
-        SmallPtrSet<const FunctionDecl*, 8> Visited;
+        SmallPtrSet<const FunctionDecl *, 8> Visited;
         if (FunctionBodyHasSideEffects(FD, Ctx, Visited))
           return true;
       } else {
@@ -887,8 +887,10 @@ static bool HasSideEffectsForAssume(const Expr *E, const ASTContext &Ctx) {
       }
 
       // Check nested calls inside arguments (e.g. foo(bar())).
-      for (const Expr *Arg : CE->arguments())
-        Worklist.push_back(Arg);
+      for (const Stmt *Child : CE->children()) {
+        if (const Expr *ChildExpr = dyn_cast_or_null<Expr>(Child))
+          Worklist.push_back(ChildExpr);
+      }
     } else {
       for (const Stmt *Child : SubExpr->children()) {
         if (const Expr *ChildExpr = dyn_cast_or_null<Expr>(Child))

>From 4c31da5c517db59da3c95c9f0c2920d8d83adcd5 Mon Sep 17 00:00:00 2001
From: Arendelle <Arendelle_ at outlook.com>
Date: Sun, 26 Apr 2026 16:06:34 +0800
Subject: [PATCH 04/12] bugfix: handle virtual function

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

diff --git a/clang/lib/Sema/SemaStmtAttr.cpp b/clang/lib/Sema/SemaStmtAttr.cpp
index db2dd812114ad..c2836c4903e3f 100644
--- a/clang/lib/Sema/SemaStmtAttr.cpp
+++ b/clang/lib/Sema/SemaStmtAttr.cpp
@@ -879,6 +879,11 @@ static bool HasSideEffectsForAssume(const Expr *E, const ASTContext &Ctx) {
 
     if (const CallExpr *CE = dyn_cast<CallExpr>(SubExpr)) {
       if (const FunctionDecl *FD = CE->getDirectCallee()) {
+        if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD)) {
+          if (MD->isVirtual())
+            return true;
+        }
+
         SmallPtrSet<const FunctionDecl *, 8> Visited;
         if (FunctionBodyHasSideEffects(FD, Ctx, Visited))
           return true;

>From a34121b702926f1e87ef08d7afe80aab93663b21 Mon Sep 17 00:00:00 2001
From: Arendelle <Arendelle_ at outlook.com>
Date: Sun, 26 Apr 2026 16:14:10 +0800
Subject: [PATCH 05/12] bugfix: handle destructor

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

diff --git a/clang/lib/Sema/SemaStmtAttr.cpp b/clang/lib/Sema/SemaStmtAttr.cpp
index c2836c4903e3f..858316ae9d688 100644
--- a/clang/lib/Sema/SemaStmtAttr.cpp
+++ b/clang/lib/Sema/SemaStmtAttr.cpp
@@ -810,6 +810,9 @@ static bool FunctionBodyHasSideEffects(const FunctionDecl *FD,
   if (FD->hasAttr<ConstAttr>() || FD->hasAttr<PureAttr>())
     return false;
 
+  if (isa<CXXDestructorDecl>(FD))
+    return true;
+
   const Stmt *Body = FD->getBody();
   if (!Body)
     return true;
@@ -884,6 +887,9 @@ static bool HasSideEffectsForAssume(const Expr *E, const ASTContext &Ctx) {
             return true;
         }
 
+        if (isa<CXXDestructorDecl>(FD))
+          return true;
+
         SmallPtrSet<const FunctionDecl *, 8> Visited;
         if (FunctionBodyHasSideEffects(FD, Ctx, Visited))
           return true;

>From cea72a92500c9717d36abeaa0de4e1b1b4c8158c Mon Sep 17 00:00:00 2001
From: Arendelle <Arendelle_ at outlook.com>
Date: Sun, 26 Apr 2026 16:21:59 +0800
Subject: [PATCH 06/12] this may helps to v-destructor

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

diff --git a/clang/lib/Sema/SemaStmtAttr.cpp b/clang/lib/Sema/SemaStmtAttr.cpp
index 858316ae9d688..c14fdd0527b5f 100644
--- a/clang/lib/Sema/SemaStmtAttr.cpp
+++ b/clang/lib/Sema/SemaStmtAttr.cpp
@@ -882,14 +882,14 @@ static bool HasSideEffectsForAssume(const Expr *E, const ASTContext &Ctx) {
 
     if (const CallExpr *CE = dyn_cast<CallExpr>(SubExpr)) {
       if (const FunctionDecl *FD = CE->getDirectCallee()) {
+        if (isa<CXXDestructorDecl>(FD))
+          return true;
+
         if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD)) {
           if (MD->isVirtual())
             return true;
         }
 
-        if (isa<CXXDestructorDecl>(FD))
-          return true;
-
         SmallPtrSet<const FunctionDecl *, 8> Visited;
         if (FunctionBodyHasSideEffects(FD, Ctx, Visited))
           return true;

>From e29bfab97415794f8b2e2dd1b88980629ebd898f Mon Sep 17 00:00:00 2001
From: Arendelle <Arendelle_ at outlook.com>
Date: Sun, 26 Apr 2026 17:21:17 +0800
Subject: [PATCH 07/12] fix tests

---
 clang/test/SemaCXX/cxx23-assume.cpp | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/clang/test/SemaCXX/cxx23-assume.cpp b/clang/test/SemaCXX/cxx23-assume.cpp
index a594a1a44337b..e7c078df61cff 100644
--- a/clang/test/SemaCXX/cxx23-assume.cpp
+++ b/clang/test/SemaCXX/cxx23-assume.cpp
@@ -13,7 +13,7 @@ void IsActOnFinishFullExprCalled() {
   // Do not add other test cases to this function.
   // Make sure `ActOnFinishFullExpr` is called and creates `ExprWithCleanups`
   // to avoid assertion failure.
-  [[assume(B{})]]; // expected-warning {{assumption is ignored because it contains (potential) side-effects}} // ext-warning {{C++23 extension}}
+  [[assume(B{})]]; // ext-warning {{C++23 extension}}
 }
 
 template <bool cond>
@@ -38,7 +38,7 @@ bool f2();
 
 template <typename T>
 constexpr void f3() {
-  [[assume(T{})]]; // expected-error {{not contextually convertible to 'bool'}} expected-warning {{assumption is ignored because it contains (potential) side-effects}} ext-warning {{C++23 extension}}
+  [[assume(T{})]]; // expected-error {{not contextually convertible to 'bool'}} ext-warning {{C++23 extension}}
 }
 
 void g(int x) {
@@ -53,8 +53,8 @@ void g(int x) {
   [[assume((x = 3))]]; // expected-warning {{assumption is ignored because it contains (potential) side-effects}} // ext-warning {{C++23 extension}}
   [[assume(x++)]]; // expected-warning {{assumption is ignored because it contains (potential) side-effects}} // ext-warning {{C++23 extension}}
   [[assume(++x)]]; // expected-warning {{assumption is ignored because it contains (potential) side-effects}} // ext-warning {{C++23 extension}}
-  [[assume([]{ return true; }())]]; // expected-warning {{assumption is ignored because it contains (potential) side-effects}} // ext-warning {{C++23 extension}}
-  [[assume(B{})]]; // expected-warning {{assumption is ignored because it contains (potential) side-effects}} // ext-warning {{C++23 extension}}
+  [[assume([]{ return true; }())]]; // ext-warning {{C++23 extension}}
+  [[assume(B{})]]; // ext-warning {{C++23 extension}}
   [[assume((1, 2))]]; // expected-warning {{has no effect}} // ext-warning {{C++23 extension}}
 
   f3<A>(); // expected-note {{in instantiation of}}
@@ -88,7 +88,7 @@ constexpr bool i() { // ext-error {{never produces a constant expression}}
 }
 
 constexpr bool j(bool b) {
-  [[assume(b)]]; // expected-note {{assumption evaluated to false}} ext-warning {{C++23 extension}}
+  [[assume(B{})]]; // ext-warning {{C++23 extension}}
   return true;
 }
 
@@ -101,7 +101,7 @@ static_assert(S<false>{}.g<A>()); // expected-error {{not an integral constant e
 
 template <typename T>
 constexpr bool f4() {
-  [[assume(!T{})]]; // expected-error {{invalid argument type 'D'}} // expected-warning 2 {{assumption is ignored because it contains (potential) side-effects}} ext-warning {{C++23 extension}}
+  [[assume(!T{})]]; // expected-error {{invalid argument type 'D'}} ext-warning {{C++23 extension}}
   return sizeof(T) == sizeof(int);
 }
 

>From d41b93494e0e84975ab6d3c04b2b11242254f2bc Mon Sep 17 00:00:00 2001
From: Arendelle <Arendelle_ at outlook.com>
Date: Sun, 26 Apr 2026 21:16:04 +0800
Subject: [PATCH 08/12] add missing llvm namespace

---
 clang/lib/Sema/SemaStmtAttr.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/Sema/SemaStmtAttr.cpp b/clang/lib/Sema/SemaStmtAttr.cpp
index c14fdd0527b5f..ea0eae8c29b87 100644
--- a/clang/lib/Sema/SemaStmtAttr.cpp
+++ b/clang/lib/Sema/SemaStmtAttr.cpp
@@ -806,7 +806,7 @@ ExprResult Sema::ActOnCXXAssumeAttr(Stmt *St, const ParsedAttr &A,
 /// \p Visited tracks the current call chain to prevent infinite recursion.
 static bool FunctionBodyHasSideEffects(const FunctionDecl *FD,
                                         const ASTContext &Ctx,
-                                        SmallPtrSetImpl<const FunctionDecl *> &Visited) {
+                                        llvm::SmallPtrSetImpl<const FunctionDecl *> &Visited) {
   if (FD->hasAttr<ConstAttr>() || FD->hasAttr<PureAttr>())
     return false;
 

>From 7eab8170f288fc748a1698859923557af82cc16d Mon Sep 17 00:00:00 2001
From: Arendelle <Arendelle_ at outlook.com>
Date: Sun, 26 Apr 2026 21:24:40 +0800
Subject: [PATCH 09/12] add missing llvm namespace for SmallPtrSet

---
 clang/lib/Sema/SemaStmtAttr.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/Sema/SemaStmtAttr.cpp b/clang/lib/Sema/SemaStmtAttr.cpp
index ea0eae8c29b87..c845bf7ff209f 100644
--- a/clang/lib/Sema/SemaStmtAttr.cpp
+++ b/clang/lib/Sema/SemaStmtAttr.cpp
@@ -890,7 +890,7 @@ static bool HasSideEffectsForAssume(const Expr *E, const ASTContext &Ctx) {
             return true;
         }
 
-        SmallPtrSet<const FunctionDecl *, 8> Visited;
+        llvm::SmallPtrSet<const FunctionDecl *, 8> Visited;
         if (FunctionBodyHasSideEffects(FD, Ctx, Visited))
           return true;
       } else {

>From d0e51bf6261a4e7da6bce59e6cecea6758a665d1 Mon Sep 17 00:00:00 2001
From: Arendelle <Arendelle_ at outlook.com>
Date: Sun, 26 Apr 2026 22:58:39 +0800
Subject: [PATCH 10/12] fix test cxx23-assume.cpp

---
 clang/test/SemaCXX/cxx23-assume.cpp | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/clang/test/SemaCXX/cxx23-assume.cpp b/clang/test/SemaCXX/cxx23-assume.cpp
index e7c078df61cff..ed49dfac2f5a3 100644
--- a/clang/test/SemaCXX/cxx23-assume.cpp
+++ b/clang/test/SemaCXX/cxx23-assume.cpp
@@ -58,7 +58,7 @@ void g(int x) {
   [[assume((1, 2))]]; // expected-warning {{has no effect}} // ext-warning {{C++23 extension}}
 
   f3<A>(); // expected-note {{in instantiation of}}
-  f3<B>(); // expected-note {{in instantiation of}}
+  f3<B>();
   [[assume]]; // expected-error {{takes one argument}}
   [[assume(z)]]; // expected-error {{undeclared identifier}}
   [[assume(A{})]]; // expected-error {{not contextually convertible to 'bool'}}
@@ -94,7 +94,7 @@ constexpr bool j(bool b) {
 
 static_assert(i()); // expected-error {{not an integral constant expression}} expected-note {{in call to}}
 static_assert(j(true));
-static_assert(j(false)); // expected-error {{not an integral constant expression}} expected-note {{in call to}}
+static_assert(j(false));
 static_assert(S<true>{}.g<char>());
 static_assert(S<false>{}.g<A>()); // expected-error {{not an integral constant expression}} expected-note {{in call to}}
 
@@ -106,8 +106,8 @@ constexpr bool f4() {
 }
 
 template <typename T>
-concept C = f4<T>(); // expected-note 3 {{in instantiation of}}
-                     // expected-note at -1 3 {{while substituting}}
+concept C = f4<T>(); // expected-note {{in instantiation of}}
+                     // expected-note at -1 {{while substituting}}
                      // expected-error at -2 {{resulted in a non-constant expression}}
                      // expected-note at -3 {{because substituted constraint expression is ill-formed: substitution into constraint expression resulted in a non-constant expression}}
 
@@ -131,8 +131,8 @@ constexpr int f5() requires C<T> { return 1; } // expected-note {{while checking
                                                // expected-note at -1 {{candidate template ignored}}
 
 template <typename T>
-constexpr int f5() requires (!C<T>) { return 2; } // expected-note 3 {{while checking the satisfaction}} \
-                                                  // expected-note 3 {{while substituting template arguments}} \
+constexpr int f5() requires (!C<T>) { return 2; } // expected-note {{while checking the satisfaction}} \
+                                                  // expected-note {{while substituting template arguments}} \
                                                   // expected-note {{candidate template ignored}}
 
 static_assert(f5<int>() == 1);
@@ -141,8 +141,8 @@ static_assert(f5<D>() == 1); // expected-note 2 {{while checking constraint sati
                              // expected-error at -2 {{no matching function for call}}
 
 static_assert(f5<double>() == 2);
-static_assert(f5<E>() == 1); // expected-note {{while checking constraint satisfaction}} expected-note {{while substituting deduced template arguments}}
-static_assert(f5<F>() == 2); // expected-note {{while checking constraint satisfaction}} expected-note {{while substituting deduced template arguments}}
+static_assert(f5<E>() == 1);
+static_assert(f5<F>() == 2);
 
 // Do not validate assumptions whose evaluation would have side-effects.
 constexpr int foo() {

>From 1dfd53c3d70cb4d90e9aaf03ba8ddc6ce846e3b1 Mon Sep 17 00:00:00 2001
From: Arendelle <Arendelle_ at outlook.com>
Date: Sun, 26 Apr 2026 23:45:46 +0800
Subject: [PATCH 11/12] more tests

---
 clang/test/SemaCXX/cxx23-assume.cpp | 37 +++++++++++++++++++++++++++++
 1 file changed, 37 insertions(+)

diff --git a/clang/test/SemaCXX/cxx23-assume.cpp b/clang/test/SemaCXX/cxx23-assume.cpp
index ed49dfac2f5a3..8bd0a72943a97 100644
--- a/clang/test/SemaCXX/cxx23-assume.cpp
+++ b/clang/test/SemaCXX/cxx23-assume.cpp
@@ -178,6 +178,43 @@ int foo () {
 }
 }
 
+namespace assume_side_effect_analysis {
+bool no_side_effects() { return true; }
+
+bool nested_no_side_effects() { return no_side_effects(); }
+
+bool side_effects(int &x) { return ++x; }
+
+bool nested_side_effects(int &x) { return side_effects(x); }
+
+bool declared_pure() __attribute__((pure));
+bool declared_const() __attribute__((const));
+
+bool recursive_b();
+bool recursive_a() { return recursive_b(); }
+bool recursive_b() { return recursive_a(); }
+
+struct V {
+  virtual bool f() { return true; }
+};
+
+void test() {
+  int x = 0;
+  V v;
+  V &vr = v;
+  bool (*fp)() = no_side_effects;
+
+  [[assume(no_side_effects())]]; // ext-warning {{C++23 extension}}
+  [[assume(nested_no_side_effects())]]; // ext-warning {{C++23 extension}}
+  [[assume(nested_side_effects(x))]]; // expected-warning {{assumption is ignored because it contains (potential) side-effects}} ext-warning {{C++23 extension}}
+  [[assume(declared_pure())]]; // ext-warning {{C++23 extension}}
+  [[assume(declared_const())]]; // ext-warning {{C++23 extension}}
+  [[assume(fp())]]; // expected-warning {{assumption is ignored because it contains (potential) side-effects}} ext-warning {{C++23 extension}}
+  [[assume(recursive_a())]]; // expected-warning {{assumption is ignored because it contains (potential) side-effects}} ext-warning {{C++23 extension}}
+  [[assume(vr.f())]]; // expected-warning {{assumption is ignored because it contains (potential) side-effects}} ext-warning {{C++23 extension}}
+}
+} // namespace assume_side_effect_analysis
+
 namespace GH114787 {
 
 // FIXME: Correct the C++26 value

>From 6289ca0f8489a902bfbdc5067202d509f62fe711 Mon Sep 17 00:00:00 2001
From: Arendelle <Arendelle_ at outlook.com>
Date: Mon, 27 Apr 2026 09:17:56 +0800
Subject: [PATCH 12/12] fix test (#1)

* fix test

* fix test

* fix test
---
 clang/test/SemaCXX/cxx23-assume.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/test/SemaCXX/cxx23-assume.cpp b/clang/test/SemaCXX/cxx23-assume.cpp
index 8bd0a72943a97..abf6fdbb8a35b 100644
--- a/clang/test/SemaCXX/cxx23-assume.cpp
+++ b/clang/test/SemaCXX/cxx23-assume.cpp
@@ -88,13 +88,13 @@ constexpr bool i() { // ext-error {{never produces a constant expression}}
 }
 
 constexpr bool j(bool b) {
-  [[assume(B{})]]; // ext-warning {{C++23 extension}}
+  [[assume(b)]]; // expected-note {{assumption evaluated to false}} ext-warning {{C++23 extension}}
   return true;
 }
 
 static_assert(i()); // expected-error {{not an integral constant expression}} expected-note {{in call to}}
 static_assert(j(true));
-static_assert(j(false));
+static_assert(j(false)); // expected-error {{not an integral constant expression}} expected-note {{in call to}}
 static_assert(S<true>{}.g<char>());
 static_assert(S<false>{}.g<A>()); // expected-error {{not an integral constant expression}} expected-note {{in call to}}
 



More information about the cfe-commits mailing list