[clang] [clang][ExprConst] Add `EvalStatus::DiagEmitted` flag (PR #203838)
Timm Baeder via cfe-commits
cfe-commits at lists.llvm.org
Mon Jun 15 04:52:45 PDT 2026
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>
Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/203838 at github.com>
https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/203838
>From 34609f76e734d0ec8cb86819725ec62c84c5b580 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Mon, 15 Jun 2026 07:19:24 +0200
Subject: [PATCH 1/2] DiagEmmitted flag
---
clang/include/clang/AST/Decl.h | 2 +-
clang/include/clang/AST/Expr.h | 9 ++++++---
clang/lib/AST/ByteCode/Compiler.cpp | 6 ++----
clang/lib/AST/ByteCode/State.cpp | 3 +++
clang/lib/AST/ComputeDependence.cpp | 6 ++----
clang/lib/AST/Decl.cpp | 16 +++++++++-------
clang/lib/AST/ExprConstant.cpp | 24 +++++++++---------------
clang/lib/Sema/SemaExpr.cpp | 4 +---
8 files changed, 33 insertions(+), 37 deletions(-)
diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index 620206612f98f..e0623d0cb324d 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -1426,7 +1426,7 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> {
APValue *evaluateValue() const;
private:
- APValue *evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> &Notes,
+ APValue *evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> *Notes,
bool IsConstantInitialization) const;
public:
diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h
index eeac69cb1d0eb..7c94c4d35641c 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -619,6 +619,10 @@ class Expr : public ValueStmt {
/// Likewise, INT_MAX + 1 can be folded to INT_MIN, but has UB.
bool HasUndefinedBehavior = false;
+ /// Whether any diagnostic has been emitted. This is set regardless of
+ /// whether @ref #Diag is set or not.
+ bool DiagEmitted = false;
+
/// Diag - If this is non-null, it will be filled in with a stack of notes
/// indicating why evaluation failed (or why it failed to produce a constant
/// expression).
@@ -735,9 +739,8 @@ class Expr : public ValueStmt {
/// initializer of the given declaration. Returns true if the initializer
/// can be folded to a constant, and produces any relevant notes. In C++11,
/// notes will be produced if the expression is not a constant expression.
- bool EvaluateAsInitializer(APValue &Result, const ASTContext &Ctx,
- const VarDecl *VD,
- SmallVectorImpl<PartialDiagnosticAt> &Notes,
+ bool EvaluateAsInitializer(const ASTContext &Ctx, const VarDecl *VD,
+ EvalResult &Result,
bool IsConstantInitializer) const;
/// EvaluateWithSubstitution - Evaluate an expression as if from the context
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 8ea42fea03bee..8c988b937580a 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -7795,10 +7795,8 @@ bool Compiler<Emitter>::visitDeclRef(const ValueDecl *D, const Expr *E) {
// Whether or not the evaluation is successul doesn't really matter
// here -- we will create a global variable in any case, and that
// will have the state of initializer evaluation attached.
- APValue V;
- SmallVector<PartialDiagnosticAt> Notes;
- (void)Init->EvaluateAsInitializer(V, Ctx.getASTContext(), VD, Notes,
- true);
+ Expr::EvalResult Result;
+ (void)Init->EvaluateAsInitializer(Ctx.getASTContext(), VD, Result, true);
return this->visitDeclRef(D, E);
}
return revisit(VD, !VD->isConstexpr() && DeclType->isReferenceType());
diff --git a/clang/lib/AST/ByteCode/State.cpp b/clang/lib/AST/ByteCode/State.cpp
index e612c6863e60f..e62b77272046c 100644
--- a/clang/lib/AST/ByteCode/State.cpp
+++ b/clang/lib/AST/ByteCode/State.cpp
@@ -25,6 +25,7 @@ OptionalDiagnostic State::FFDiag(SourceLocation Loc, diag::kind DiagId,
OptionalDiagnostic State::FFDiag(const Expr *E, diag::kind DiagId,
unsigned ExtraNotes) {
+ EvalStatus.DiagEmitted = true;
if (EvalStatus.Diag)
return diag(E->getExprLoc(), DiagId, ExtraNotes, false);
setActiveDiagnostic(false);
@@ -33,6 +34,7 @@ OptionalDiagnostic State::FFDiag(const Expr *E, diag::kind DiagId,
OptionalDiagnostic State::FFDiag(SourceInfo SI, diag::kind DiagId,
unsigned ExtraNotes) {
+ EvalStatus.DiagEmitted = true;
if (EvalStatus.Diag)
return diag(SI.getLoc(), DiagId, ExtraNotes, false);
setActiveDiagnostic(false);
@@ -41,6 +43,7 @@ OptionalDiagnostic State::FFDiag(SourceInfo SI, diag::kind DiagId,
OptionalDiagnostic State::CCEDiag(SourceLocation Loc, diag::kind DiagId,
unsigned ExtraNotes) {
+ EvalStatus.DiagEmitted = true;
// Don't override a previous diagnostic. Don't bother collecting
// diagnostics if we're evaluating for overflow.
if (!EvalStatus.Diag || !EvalStatus.Diag->empty()) {
diff --git a/clang/lib/AST/ComputeDependence.cpp b/clang/lib/AST/ComputeDependence.cpp
index 34167eee8d8f2..95cfd19c0bcc8 100644
--- a/clang/lib/AST/ComputeDependence.cpp
+++ b/clang/lib/AST/ComputeDependence.cpp
@@ -57,11 +57,9 @@ ExprDependence clang::computeDependence(UnaryOperator *E,
if (Ctx.getLangOpts().CPlusPlus && E->getOpcode() == UO_AddrOf &&
!(Dep & ExprDependence::Value)) {
Expr::EvalResult Result;
- SmallVector<PartialDiagnosticAt, 8> Diag;
- Result.Diag = &Diag;
// FIXME: This doesn't enforce the C++98 constant expression rules.
- if (E->getSubExpr()->EvaluateAsConstantExpr(Result, Ctx) && Diag.empty() &&
- Result.Val.isLValue()) {
+ if (E->getSubExpr()->EvaluateAsConstantExpr(Result, Ctx) &&
+ !Result.DiagEmitted && Result.Val.isLValue()) {
auto *VD = Result.Val.getLValueBase().dyn_cast<const ValueDecl *>();
if (VD && VD->isTemplated()) {
auto *VarD = dyn_cast<VarDecl>(VD);
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index b797ebfa1a7e1..7ab4235717dde 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -2552,11 +2552,10 @@ EvaluatedStmt *VarDecl::getEvaluatedStmt() const {
}
APValue *VarDecl::evaluateValue() const {
- SmallVector<PartialDiagnosticAt, 8> Notes;
- return evaluateValueImpl(Notes, hasConstantInitialization());
+ return evaluateValueImpl(/*Notes=*/nullptr, hasConstantInitialization());
}
-APValue *VarDecl::evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> &Notes,
+APValue *VarDecl::evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> *Notes,
bool IsConstantInitialization) const {
EvaluatedStmt *Eval = ensureEvaluatedStmt();
@@ -2577,8 +2576,11 @@ APValue *VarDecl::evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> &Notes,
Eval->IsEvaluating = true;
ASTContext &Ctx = getASTContext();
- bool Result = Init->EvaluateAsInitializer(Eval->Evaluated, Ctx, this, Notes,
- IsConstantInitialization);
+ Expr::EvalResult EStatus;
+ EStatus.Diag = Notes;
+ bool Result =
+ Init->EvaluateAsInitializer(Ctx, this, EStatus, IsConstantInitialization);
+ Eval->Evaluated = std::move(EStatus.Val);
// In C++, or in C23 if we're initialising a 'constexpr' variable, this isn't
// a constant initializer if we produced notes. In that case, we can't keep
@@ -2587,7 +2589,7 @@ APValue *VarDecl::evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> &Notes,
if (IsConstantInitialization &&
(Ctx.getLangOpts().CPlusPlus ||
(isConstexpr() && Ctx.getLangOpts().C23)) &&
- !Notes.empty())
+ EStatus.DiagEmitted)
Result = false;
// Ensure the computed APValue is cleaned up later if evaluation succeeded,
@@ -2656,7 +2658,7 @@ bool VarDecl::checkForConstantInitialization(
// Evaluate the initializer to check whether it's a constant expression.
Eval->HasConstantInitialization =
- evaluateValueImpl(Notes, true) && Notes.empty();
+ evaluateValueImpl(&Notes, true) && Notes.empty();
// If evaluation as a constant initializer failed, allow re-evaluation as a
// non-constant initializer if we later find we want the value.
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 1642c41a99a2f..f4cd9becbfab8 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -21532,9 +21532,8 @@ bool Expr::EvaluateAsConstantExpr(EvalResult &Result, const ASTContext &Ctx,
return true;
}
-bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx,
- const VarDecl *VD,
- SmallVectorImpl<PartialDiagnosticAt> &Notes,
+bool Expr::EvaluateAsInitializer(const ASTContext &Ctx, const VarDecl *VD,
+ Expr::EvalResult &EStatus,
bool IsConstantInitialization) const {
assert(!isValueDependent() &&
"Expression evaluator can't be called on a dependent expression.");
@@ -21547,15 +21546,12 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx,
return Name;
});
- Expr::EvalStatus EStatus;
- EStatus.Diag = &Notes;
-
EvalInfo Info(Ctx, EStatus,
(IsConstantInitialization &&
(Ctx.getLangOpts().CPlusPlus || Ctx.getLangOpts().C23))
? EvaluationMode::ConstantExpression
: EvaluationMode::ConstantFold);
- Info.setEvaluatingDecl(VD, Value);
+ Info.setEvaluatingDecl(VD, EStatus.Val);
Info.InConstantContext = IsConstantInitialization;
SourceLocation DeclLoc = VD->getLocation();
@@ -21563,10 +21559,10 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx,
if (Info.EnableNewConstInterp) {
auto &InterpCtx = Ctx.getInterpContext();
- if (!InterpCtx.evaluateAsInitializer(Info, VD, this, Value))
+ if (!InterpCtx.evaluateAsInitializer(Info, VD, this, EStatus.Val))
return false;
- return CheckConstantExpression(Info, DeclLoc, DeclTy, Value,
+ return CheckConstantExpression(Info, DeclLoc, DeclTy, EStatus.Val,
ConstantExprKind::Normal);
} else {
LValue LVal;
@@ -21583,7 +21579,7 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx,
// serialization code calls ParmVarDecl::getDefaultArg() which strips the
// outermost FullExpr, such as ExprWithCleanups.
FullExpressionRAII Scope(Info);
- if (!EvaluateInPlace(Value, Info, LVal, this,
+ if (!EvaluateInPlace(EStatus.Val, Info, LVal, this,
/*AllowNonLiteralTypes=*/true) ||
EStatus.HasSideEffects)
return false;
@@ -21597,7 +21593,7 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx,
llvm_unreachable("Unhandled cleanup; missing full expression marker?");
}
- return CheckConstantExpression(Info, DeclLoc, DeclTy, Value,
+ return CheckConstantExpression(Info, DeclLoc, DeclTy, EStatus.Val,
ConstantExprKind::Normal) &&
CheckMemoryLeaks(Info);
}
@@ -22292,17 +22288,15 @@ bool Expr::isCXX11ConstantExpr(const ASTContext &Ctx, APValue *Result) const {
// Build evaluation settings.
Expr::EvalStatus Status;
- SmallVector<PartialDiagnosticAt, 8> Diags;
- Status.Diag = &Diags;
EvalInfo Info(Ctx, Status, EvaluationMode::ConstantExpression);
bool IsConstExpr =
::EvaluateAsRValue(Info, this, Result ? *Result : Scratch) &&
- // FIXME: We don't produce a diagnostic for this, but the callers that
+ // NOTE: We don't produce a diagnostic for this, but the callers that
// call us on arbitrary full-expressions should generally not care.
Info.discardCleanups() && !Status.HasSideEffects;
- return IsConstExpr && Diags.empty();
+ return IsConstExpr && !Status.DiagEmitted;
}
bool Expr::EvaluateWithSubstitution(APValue &Value, ASTContext &Ctx,
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index f2745425588f5..675d0bd389e28 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -18340,12 +18340,10 @@ ExprResult Sema::CheckForImmediateInvocation(ExprResult E, FunctionDecl *Decl) {
// immediate invocation.
APValue Cached;
auto CheckConstantExpressionAndKeepResult = [&]() {
- llvm::SmallVector<PartialDiagnosticAt, 8> Notes;
Expr::EvalResult Eval;
- Eval.Diag = &Notes;
bool Res = E.get()->EvaluateAsConstantExpr(
Eval, getASTContext(), ConstantExprKind::ImmediateInvocation);
- if (Res && Notes.empty()) {
+ if (Res && !Eval.DiagEmitted) {
Cached = std::move(Eval.Val);
return true;
}
>From 07b9a1b91329103e691b4d097537c1a0d9694fca Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Mon, 15 Jun 2026 13:31:56 +0200
Subject: [PATCH 2/2] Save DiagEmitted state when speculating
---
clang/lib/AST/ByteCode/Interp.h | 2 ++
clang/lib/AST/ByteCode/InterpState.h | 1 +
clang/lib/AST/ExprConstant.cpp | 5 ++++-
3 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index b6377e22f0db6..5c6d305f9ecd9 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -3640,6 +3640,7 @@ inline bool PushIgnoreDiags(InterpState &S, CodePtr OpPC) {
return true;
assert(S.PrevDiags == nullptr);
S.PrevDiags = S.getEvalStatus().Diag;
+ S.PrevDiagsEmitted = S.getEvalStatus().DiagEmitted;
S.getEvalStatus().Diag = nullptr;
assert(!S.diagnosing());
return true;
@@ -3650,6 +3651,7 @@ inline bool PopIgnoreDiags(InterpState &S, CodePtr OpPC) {
--S.DiagIgnoreDepth;
if (S.DiagIgnoreDepth == 0) {
S.getEvalStatus().Diag = S.PrevDiags;
+ S.getEvalStatus().DiagEmitted = S.PrevDiagsEmitted;
S.PrevDiags = nullptr;
}
return true;
diff --git a/clang/lib/AST/ByteCode/InterpState.h b/clang/lib/AST/ByteCode/InterpState.h
index b6ef985095907..27ecac4619931 100644
--- a/clang/lib/AST/ByteCode/InterpState.h
+++ b/clang/lib/AST/ByteCode/InterpState.h
@@ -162,6 +162,7 @@ class InterpState final : public State, public SourceMapper {
/// Things needed to do speculative execution.
SmallVectorImpl<PartialDiagnosticAt> *PrevDiags = nullptr;
+ bool PrevDiagsEmitted;
#ifndef NDEBUG
unsigned SpeculationDepth = 0;
#endif
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index f4cd9becbfab8..255996efca101 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -1143,8 +1143,10 @@ namespace {
void keepDiagnostics() { Enabled = false; }
~FoldConstant() {
if (Enabled && HadNoPriorDiags && !Info.EvalStatus.Diag->empty() &&
- !Info.EvalStatus.HasSideEffects)
+ !Info.EvalStatus.HasSideEffects) {
Info.EvalStatus.Diag->clear();
+ Info.EvalStatus.DiagEmitted = false;
+ }
Info.EvalMode = OldMode;
}
};
@@ -8407,6 +8409,7 @@ class ExprEvaluatorBase
{
SpeculativeEvaluationRAII Speculate(Info, &Diag);
Diag.clear();
+ Info.EvalStatus.DiagEmitted = false;
StmtVisitorTy::Visit(E->getTrueExpr());
if (Diag.empty())
return;
More information about the cfe-commits
mailing list