diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index c24d4124d9fc5..64a3831a37eb4 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -61,6 +61,7 @@ class CompoundStmt; class DependentFunctionTemplateSpecializationInfo; class EnumDecl; class Expr; +struct EvalStatus; class FunctionTemplateDecl; class FunctionTemplateSpecializationInfo; class FunctionTypeLoc; @@ -1412,7 +1413,7 @@ class VarDecl : public DeclaratorDecl, public Redeclarable { APValue *evaluateValue() const; private: - APValue *evaluateValueImpl(SmallVectorImpl &Notes, + APValue *evaluateValueImpl(EvalStatus &EStatus, bool IsConstantInitialization) const; public: @@ -1446,7 +1447,7 @@ class VarDecl : public DeclaratorDecl, public Redeclarable { /// constant initializer. Should only be called once, after completing the /// definition of the variable. bool checkForConstantInitialization( - SmallVectorImpl &Notes) const; + SmallVectorImpl *Notes) const; void setInitStyle(InitializationStyle Style) { VarDeclBits.InitStyle = Style; diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 3d7e674501ea0..709ca2f20ced1 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -60,6 +60,47 @@ namespace clang { class ValueDecl; class WarnUnusedResultAttr; + /// EvalStatus is a struct with detailed info about an evaluation in progress. + struct EvalStatus { + /// Whether the evaluated expression has side effects. + /// For example, (f() && 0) can be folded, but it still has side effects. + bool HasSideEffects = false; + + /// Whether the evaluation hit undefined behavior. + /// For example, 1.0 / 0.0 can be folded to Inf, but has undefined behavior. + /// Likewise, INT_MAX + 1 can be folded to INT_MIN, but has UB. + bool HasUndefinedBehavior = false; + + bool HasFFDiagnostic = false; + bool HasCCEDiagnostic = 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). + /// If the expression is unfoldable, the notes will indicate why it's not + /// foldable. If the expression is foldable, but not a constant expression, + /// the notes will describes why it isn't a constant expression. If the + /// expression *is* a constant expression, no notes will be produced. + /// + /// FIXME: this causes significant performance concerns and should be + /// refactored at some point. Not all evaluations of the constant + /// expression interpreter will display the given diagnostics, this means + /// those kinds of uses are paying the expense of generating a diagnostic + /// (which may include expensive operations like converting APValue objects + /// to a string representation). + SmallVectorImpl *Diag = nullptr; + + EvalStatus() = default; + + /// Return true if the evaluated expression has + /// side effects. + bool hasSideEffects() const { return HasSideEffects; } + + bool hasAnyDiagnostic() const { + return HasFFDiagnostic || HasCCEDiagnostic; + } + }; + /// A simple array of base specifiers. typedef SmallVector CXXCastPath; @@ -605,42 +646,6 @@ class Expr : public ValueStmt { /// expression is a member pointer constant. const ValueDecl *getAsBuiltinConstantDeclRef(const ASTContext &Context) const; - /// EvalStatus is a struct with detailed info about an evaluation in progress. - struct EvalStatus { - /// Whether the evaluated expression has side effects. - /// For example, (f() && 0) can be folded, but it still has side effects. - bool HasSideEffects = false; - - /// Whether the evaluation hit undefined behavior. - /// For example, 1.0 / 0.0 can be folded to Inf, but has undefined behavior. - /// Likewise, INT_MAX + 1 can be folded to INT_MIN, but has UB. - bool HasUndefinedBehavior = 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). - /// If the expression is unfoldable, the notes will indicate why it's not - /// foldable. If the expression is foldable, but not a constant expression, - /// the notes will describes why it isn't a constant expression. If the - /// expression *is* a constant expression, no notes will be produced. - /// - /// FIXME: this causes significant performance concerns and should be - /// refactored at some point. Not all evaluations of the constant - /// expression interpreter will display the given diagnostics, this means - /// those kinds of uses are paying the expense of generating a diagnostic - /// (which may include expensive operations like converting APValue objects - /// to a string representation). - SmallVectorImpl *Diag = nullptr; - - EvalStatus() = default; - - /// Return true if the evaluated expression has - /// side effects. - bool hasSideEffects() const { - return HasSideEffects; - } - }; - /// EvalResult is a struct with detailed info about an evaluated expression. struct EvalResult : EvalStatus { /// Val - This is the value the expression can be folded to. @@ -735,8 +740,7 @@ class Expr : public ValueStmt { /// 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 &Notes, + const VarDecl *VD, EvalStatus &Status, 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 1340a84a7d44d..8cc146d8da423 100644 --- a/clang/lib/AST/ByteCode/Compiler.cpp +++ b/clang/lib/AST/ByteCode/Compiler.cpp @@ -6985,8 +6985,8 @@ bool Compiler::visitDeclRef(const ValueDecl *D, const Expr *E) { // here -- we will create a global variable in any case, and that // will have the state of initializer evaluation attached. APValue V; - SmallVector Notes; - (void)Init->EvaluateAsInitializer(V, Ctx.getASTContext(), VD, Notes, + EvalStatus EStatus; + (void)Init->EvaluateAsInitializer(V, Ctx.getASTContext(), VD, EStatus, true); return this->visitDeclRef(D, E); } diff --git a/clang/lib/AST/ByteCode/InterpState.h b/clang/lib/AST/ByteCode/InterpState.h index a13244bf383ae..d2c6c01423670 100644 --- a/clang/lib/AST/ByteCode/InterpState.h +++ b/clang/lib/AST/ByteCode/InterpState.h @@ -64,9 +64,7 @@ class InterpState final : public State, public SourceMapper { const Frame *getBottomFrame() const override { return &BottomFrame; } // Access objects from the walker context. - Expr::EvalStatus &getEvalStatus() const override { - return Parent.getEvalStatus(); - } + EvalStatus &getEvalStatus() const override { return Parent.getEvalStatus(); } ASTContext &getASTContext() const override { return Ctx.getASTContext(); } const LangOptions &getLangOpts() const { return Ctx.getASTContext().getLangOpts(); diff --git a/clang/lib/AST/ByteCode/State.cpp b/clang/lib/AST/ByteCode/State.cpp index 323231fbf8236..b68b977820a4b 100644 --- a/clang/lib/AST/ByteCode/State.cpp +++ b/clang/lib/AST/ByteCode/State.cpp @@ -84,7 +84,12 @@ PartialDiagnostic &State::addDiag(SourceLocation Loc, diag::kind DiagId) { OptionalDiagnostic State::diag(SourceLocation Loc, diag::kind DiagId, unsigned ExtraNotes, bool IsCCEDiag) { - Expr::EvalStatus &EvalStatus = getEvalStatus(); + EvalStatus &EvalStatus = getEvalStatus(); + if (IsCCEDiag) + EvalStatus.HasCCEDiagnostic = true; + else + EvalStatus.HasFFDiagnostic = true; + if (EvalStatus.Diag) { if (hasPriorDiagnostic()) { return OptionalDiagnostic(); diff --git a/clang/lib/AST/ByteCode/State.h b/clang/lib/AST/ByteCode/State.h index 0695c61c07a05..d76d4fe350f9f 100644 --- a/clang/lib/AST/ByteCode/State.h +++ b/clang/lib/AST/ByteCode/State.h @@ -88,7 +88,7 @@ class State { virtual bool hasActiveDiagnostic() = 0; virtual void setActiveDiagnostic(bool Flag) = 0; virtual void setFoldFailureDiagnostic(bool Flag) = 0; - virtual Expr::EvalStatus &getEvalStatus() const = 0; + virtual EvalStatus &getEvalStatus() const = 0; virtual ASTContext &getASTContext() const = 0; virtual bool hasPriorDiagnostic() = 0; virtual unsigned getCallStackDepth() = 0; diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index d8dffb7f5dc43..f4872eb9d5138 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -2573,11 +2573,11 @@ EvaluatedStmt *VarDecl::getEvaluatedStmt() const { } APValue *VarDecl::evaluateValue() const { - SmallVector Notes; - return evaluateValueImpl(Notes, hasConstantInitialization()); + EvalStatus ES; + return evaluateValueImpl(ES, hasConstantInitialization()); } -APValue *VarDecl::evaluateValueImpl(SmallVectorImpl &Notes, +APValue *VarDecl::evaluateValueImpl(EvalStatus &EStatus, bool IsConstantInitialization) const { EvaluatedStmt *Eval = ensureEvaluatedStmt(); @@ -2598,7 +2598,7 @@ APValue *VarDecl::evaluateValueImpl(SmallVectorImpl &Notes, Eval->IsEvaluating = true; ASTContext &Ctx = getASTContext(); - bool Result = Init->EvaluateAsInitializer(Eval->Evaluated, Ctx, this, Notes, + bool Result = Init->EvaluateAsInitializer(Eval->Evaluated, Ctx, this, EStatus, IsConstantInitialization); // In C++, or in C23 if we're initialising a 'constexpr' variable, this isn't @@ -2608,7 +2608,7 @@ APValue *VarDecl::evaluateValueImpl(SmallVectorImpl &Notes, if (IsConstantInitialization && (Ctx.getLangOpts().CPlusPlus || (isConstexpr() && Ctx.getLangOpts().C23)) && - !Notes.empty()) + EStatus.hasAnyDiagnostic()) Result = false; // Ensure the computed APValue is cleaned up later if evaluation succeeded, @@ -2662,7 +2662,7 @@ bool VarDecl::hasConstantInitialization() const { } bool VarDecl::checkForConstantInitialization( - SmallVectorImpl &Notes) const { + SmallVectorImpl *Notes) const { EvaluatedStmt *Eval = ensureEvaluatedStmt(); // If we ask for the value before we know whether we have a constant // initializer, we can compute the wrong value (for example, due to @@ -2676,8 +2676,10 @@ bool VarDecl::checkForConstantInitialization( assert(!getInit()->isValueDependent()); // Evaluate the initializer to check whether it's a constant expression. + EvalStatus EStatus; + EStatus.Diag = Notes; Eval->HasConstantInitialization = - evaluateValueImpl(Notes, true) && Notes.empty(); + evaluateValueImpl(EStatus, true) && !EStatus.hasAnyDiagnostic(); // 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 b2cb9e2b3c347..48f2020f39f76 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -798,7 +798,7 @@ namespace { ASTContext &Ctx; /// EvalStatus - Contains information about the evaluation. - Expr::EvalStatus &EvalStatus; + struct EvalStatus &EvalStatus; /// CurrentCall - The top of the constexpr call stack. CallStackFrame *CurrentCall; @@ -926,7 +926,7 @@ namespace { /// fold (not just why it's not strictly a constant expression)? bool HasFoldFailureDiagnostic; - EvalInfo(const ASTContext &C, Expr::EvalStatus &S, EvaluationMode Mode) + EvalInfo(const ASTContext &C, struct EvalStatus &S, EvaluationMode Mode) : Ctx(const_cast(C)), EvalStatus(S), CurrentCall(nullptr), CallStackDepth(0), NextCallIndex(1), StepsLeft(C.getLangOpts().ConstexprStepLimit), @@ -1107,7 +1107,7 @@ namespace { HasFoldFailureDiagnostic = Flag; } - Expr::EvalStatus &getEvalStatus() const override { return EvalStatus; } + struct EvalStatus &getEvalStatus() const override { return EvalStatus; } // If we have a prior diagnostic, it will be noting that the expression // isn't a constant expression. This diagnostic is more important, @@ -1281,7 +1281,7 @@ namespace { /// a speculative evaluation. class SpeculativeEvaluationRAII { EvalInfo *Info = nullptr; - Expr::EvalStatus OldStatus; + EvalStatus OldStatus; unsigned OldSpeculativeEvaluationDepth = 0; void moveFromAndCancel(SpeculativeEvaluationRAII &&Other) { @@ -17516,7 +17516,7 @@ static bool FastEvaluateAsRValue(const Expr *Exp, APValue &Result, return false; } -static bool hasUnacceptableSideEffect(Expr::EvalStatus &Result, +static bool hasUnacceptableSideEffect(EvalStatus &Result, Expr::SideEffectsKind SEK) { return (SEK < Expr::SE_AllowSideEffects && Result.HasSideEffects) || (SEK < Expr::SE_AllowUndefinedBehavior && Result.HasUndefinedBehavior); @@ -17668,7 +17668,7 @@ bool Expr::EvaluateAsLValue(EvalResult &Result, const ASTContext &Ctx, static bool EvaluateDestruction(const ASTContext &Ctx, APValue::LValueBase Base, APValue DestroyedValue, QualType Type, - SourceLocation Loc, Expr::EvalStatus &EStatus, + SourceLocation Loc, EvalStatus &EStatus, bool IsConstantDestruction) { EvalInfo Info(Ctx, EStatus, IsConstantDestruction ? EvaluationMode::ConstantExpression @@ -17758,9 +17758,9 @@ bool Expr::EvaluateAsConstantExpr(EvalResult &Result, const ASTContext &Ctx, } bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx, - const VarDecl *VD, - SmallVectorImpl &Notes, + const VarDecl *VD, EvalStatus &Status, bool IsConstantInitialization) const { + assert(!isValueDependent() && "Expression evaluator can't be called on a dependent expression."); assert(VD && "Need a valid VarDecl"); @@ -17772,10 +17772,7 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx, return Name; }); - Expr::EvalStatus EStatus; - EStatus.Diag = &Notes; - - EvalInfo Info(Ctx, EStatus, + EvalInfo Info(Ctx, Status, (IsConstantInitialization && (Ctx.getLangOpts().CPlusPlus || Ctx.getLangOpts().C23)) ? EvaluationMode::ConstantExpression @@ -17810,7 +17807,7 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx, FullExpressionRAII Scope(Info); if (!EvaluateInPlace(Value, Info, LVal, this, /*AllowNonLiteralTypes=*/true) || - EStatus.HasSideEffects) + Status.HasSideEffects) return false; } @@ -17829,7 +17826,7 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx, bool VarDecl::evaluateDestruction( SmallVectorImpl &Notes) const { - Expr::EvalStatus EStatus; + EvalStatus EStatus; EStatus.Diag = &Notes; // Only treat the destruction as constant destruction if we formally have @@ -17966,7 +17963,7 @@ static ICEDiag Worst(ICEDiag A, ICEDiag B) { return A.Kind >= B.Kind ? A : B; } static ICEDiag CheckEvalInICE(const Expr* E, const ASTContext &Ctx) { Expr::EvalResult EVResult; - Expr::EvalStatus Status; + EvalStatus Status; EvalInfo Info(Ctx, Status, EvaluationMode::ConstantExpression); Info.InConstantContext = true; @@ -18450,7 +18447,7 @@ Expr::getIntegerConstantExpr(const ASTContext &Ctx) const { // required to treat the expression as an ICE, so we produce the folded // value. EvalResult ExprResult; - Expr::EvalStatus Status; + EvalStatus Status; EvalInfo Info(Ctx, Status, EvaluationMode::IgnoreSideEffects); Info.InConstantContext = true; @@ -18484,9 +18481,7 @@ bool Expr::isCXX11ConstantExpr(const ASTContext &Ctx, APValue *Result) const { } // Build evaluation settings. - Expr::EvalStatus Status; - SmallVector Diags; - Status.Diag = &Diags; + EvalStatus Status; EvalInfo Info(Ctx, Status, EvaluationMode::ConstantExpression); bool IsConstExpr = @@ -18495,7 +18490,7 @@ bool Expr::isCXX11ConstantExpr(const ASTContext &Ctx, APValue *Result) const { // call us on arbitrary full-expressions should generally not care. Info.discardCleanups() && !Status.HasSideEffects; - return IsConstExpr && Diags.empty(); + return IsConstExpr && !Status.hasAnyDiagnostic(); } bool Expr::EvaluateWithSubstitution(APValue &Value, ASTContext &Ctx, @@ -18513,7 +18508,7 @@ bool Expr::EvaluateWithSubstitution(APValue &Value, ASTContext &Ctx, return Name; }); - Expr::EvalStatus Status; + EvalStatus Status; EvalInfo Info(Ctx, Status, EvaluationMode::ConstantExpressionUnevaluated); Info.InConstantContext = true; @@ -18587,7 +18582,7 @@ bool Expr::isPotentialConstantExpr(const FunctionDecl *FD, return Name; }); - Expr::EvalStatus Status; + EvalStatus Status; Status.Diag = &Diags; EvalInfo Info(FD->getASTContext(), Status, @@ -18637,7 +18632,7 @@ bool Expr::isPotentialConstantExprUnevaluated(Expr *E, assert(!E->isValueDependent() && "Expression evaluator can't be called on a dependent expression."); - Expr::EvalStatus Status; + EvalStatus Status; Status.Diag = &Diags; EvalInfo Info(FD->getASTContext(), Status, @@ -18664,7 +18659,7 @@ bool Expr::tryEvaluateObjectSize(uint64_t &Result, ASTContext &Ctx, if (!getType()->isPointerType()) return false; - Expr::EvalStatus Status; + EvalStatus Status; EvalInfo Info(Ctx, Status, EvaluationMode::ConstantFold); return tryEvaluateBuiltinObjectSize(this, Type, Info, Result); } @@ -18722,7 +18717,7 @@ static bool EvaluateBuiltinStrLen(const Expr *E, uint64_t &Result, } std::optional Expr::tryEvaluateString(ASTContext &Ctx) const { - Expr::EvalStatus Status; + EvalStatus Status; EvalInfo Info(Ctx, Status, EvaluationMode::ConstantFold); uint64_t Result; std::string StringResult; @@ -18805,7 +18800,7 @@ bool Expr::EvaluateCharRangeAsString(APValue &Result, } bool Expr::tryEvaluateStrLen(uint64_t &Result, ASTContext &Ctx) const { - Expr::EvalStatus Status; + EvalStatus Status; EvalInfo Info(Ctx, Status, EvaluationMode::ConstantFold); if (Info.EnableNewConstInterp) diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index e10511cc7fc4e..c57fb04c585ac 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -14855,8 +14855,7 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) { << Init->getSourceRange(); } } - (void)var->checkForConstantInitialization(Notes); - Notes.clear(); + (void)var->checkForConstantInitialization(nullptr); } else if (CacheCulprit) { Notes.emplace_back(CacheCulprit->getExprLoc(), PDiag(diag::note_invalid_subexpr_in_const_expr)); @@ -14864,7 +14863,7 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) { } } else { // Evaluate the initializer to see if it's a constant initializer. - HasConstInit = var->checkForConstantInitialization(Notes); + HasConstInit = var->checkForConstantInitialization(&Notes); } if (HasConstInit) {