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; } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
