[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