[clang] baad7c3 - [clang][ExprConst] Add `EvalStatus::DiagEmitted` flag (#203838)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Jun 18 01:23:45 PDT 2026
Author: Timm Baeder
Date: 2026-06-18T10:23:40+02:00
New Revision: baad7c3238b3203c974787fc4881453b6b23a0b6
URL: https://github.com/llvm/llvm-project/commit/baad7c3238b3203c974787fc4881453b6b23a0b6
DIFF: https://github.com/llvm/llvm-project/commit/baad7c3238b3203c974787fc4881453b6b23a0b6.diff
LOG: [clang][ExprConst] Add `EvalStatus::DiagEmitted` flag (#203838)
There are some instances where we want to know _if_ a diagnostic has
been emitted, but we're not interested in the actual diagnostic.
Emitting the diagnostics can be rather costly, especially because we add
the callstack as notes.
Added:
Modified:
clang/include/clang/AST/Decl.h
clang/include/clang/AST/Expr.h
clang/lib/AST/ByteCode/Compiler.cpp
clang/lib/AST/ByteCode/Interp.h
clang/lib/AST/ByteCode/InterpState.h
clang/lib/AST/ByteCode/State.cpp
clang/lib/AST/ComputeDependence.cpp
clang/lib/AST/Decl.cpp
clang/lib/AST/ExprConstant.cpp
clang/lib/Sema/SemaExpr.cpp
Removed:
################################################################################
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 638e6ecafb295..ac5cc70018b8b 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -7806,10 +7806,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/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index f80617361f768..d1836b6b739b2 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -3643,6 +3643,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;
@@ -3653,6 +3654,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 3f27345ea774d..92073cebcd490 100644
--- a/clang/lib/AST/ByteCode/InterpState.h
+++ b/clang/lib/AST/ByteCode/InterpState.h
@@ -169,6 +169,7 @@ class InterpState final : public State, public SourceMapper {
/// Things needed to do speculative execution.
SmallVectorImpl<PartialDiagnosticAt> *PrevDiags = nullptr;
+ bool PrevDiagsEmitted = false;
#ifndef NDEBUG
unsigned SpeculationDepth = 0;
#endif
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 bc98c0d86bb65..6ac16c2b831d2 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;
}
};
@@ -8409,6 +8411,7 @@ class ExprEvaluatorBase
{
SpeculativeEvaluationRAII Speculate(Info, &Diag);
Diag.clear();
+ Info.EvalStatus.DiagEmitted = false;
StmtVisitorTy::Visit(E->getTrueExpr());
if (Diag.empty())
return;
@@ -21534,9 +21537,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.");
@@ -21549,15 +21551,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();
@@ -21565,10 +21564,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;
@@ -21585,7 +21584,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;
@@ -21599,7 +21598,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);
}
@@ -22294,17 +22293,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;
}
More information about the cfe-commits
mailing list